Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
@@ -0,0 +1,53 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
# Created by Patric Karwat, SLSE, 2016-07-27
|
||||
#
|
||||
|
||||
SET(replay_control_gui_SRCS
|
||||
replay_control_gui_plugin.h
|
||||
replay_control_gui_plugin.cpp
|
||||
replay_control_dialog.h
|
||||
replay_control_dialog.cpp
|
||||
replay_control_widget.h
|
||||
replay_control_widget.cpp
|
||||
|
||||
TopicEditor/topic_editor.h
|
||||
TopicEditor/topic_editor.cpp
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(replay_control_gui_SRCS
|
||||
replay_control_widget.ui
|
||||
|
||||
TopicEditor/topic_editor.ui
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY(WS SrpReplayControlGuiPlugin MODULE ${replay_control_gui_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(SrpReplayControlGuiPlugin
|
||||
NGLLogger
|
||||
ngltools # dtg
|
||||
SrpGuiPluginInterface
|
||||
SrpDataPluginInterface
|
||||
MrkCommon
|
||||
Qt5::Widgets
|
||||
LtsWidgets
|
||||
LtsSession
|
||||
SituationMapScriptFilterGuiPluginInterface
|
||||
LtsDataView
|
||||
LtsUtilsGuiDds
|
||||
|
||||
# TopicEditor:
|
||||
TopicEditorBase
|
||||
LtsScriptTimeline
|
||||
MrkDataPluginInterface
|
||||
MrkGuiPluginInterface
|
||||
OpmDataPluginInterface
|
||||
|
||||
PreGuiScriptCommon
|
||||
situationMap
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(SrpReplayControlGuiPlugin)
|
||||
|
||||
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Replay in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#include "topic_editor.h"
|
||||
#include "ui_topic_editor.h"
|
||||
#include "../replay_control_widget.h"
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <MarkerManagement/Gui/Common/mrk_dialog_model.h>
|
||||
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
|
||||
#include <Services/MarkerManagement/mrk.hpp>
|
||||
#include <SituationReplay/logger.h>
|
||||
#include <TopicEditorBase/topic_previewer.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||||
|
||||
|
||||
//#include <Viewer/SituationMap/Map/map.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QtMath>
|
||||
|
||||
|
||||
class QAbstractTableModel;
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
|
||||
SituationReplayEditor::SituationReplayEditor(
|
||||
MarkerManagement::MarkerManagementDialogModel *markerModel,
|
||||
const QPointer<MarkerManagement::MarkerManagementApplyFilter> &applyFilter,
|
||||
LtsSession::LtsSession *session, QWidget *parent,
|
||||
ForeignGuiInterface **essentialsViewsSrpIfc,
|
||||
SituationMap::ForeignGuiInterface **essentialsViewsSitMapIfc
|
||||
)
|
||||
: TopicEditorBase::TopicEditor(parent)
|
||||
, m_session(session)
|
||||
, m_essentialsViewsSrpIfc(essentialsViewsSrpIfc)
|
||||
, m_essentialsViewsSitMapIfc(essentialsViewsSitMapIfc)
|
||||
, m_markerModel(markerModel)
|
||||
, m_applyFilter(applyFilter)
|
||||
, m_ui(new Ui::SituationReplayEditor)
|
||||
, m_logger(getLogger("SituationReplayEditor"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->lblDuration->setText(QStringLiteral(" - "));
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setText(tr("Preview Replay"));
|
||||
if ((*m_essentialsViewsSrpIfc) == nullptr)
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setToolTip(tr("To enable replay preview, start the essentials view first."));
|
||||
else
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setToolTip(QString());
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show,
|
||||
this, &SituationReplayEditor::onPreview);
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->setText(tr("Show Map Settings"));
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->setToolTip(tr("Show this topic's map settings in Essentials View"));
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add, this, &SituationReplayEditor::onShowMapSettings);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->hide();
|
||||
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Get Map Settings"));
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setToolTip(tr("Get map settings from Essentials View and set this topic's map settings"));
|
||||
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load, this, &SituationReplayEditor::onGetSettings);
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->hide();
|
||||
|
||||
|
||||
onEssentialsViewChanged((*m_essentialsViewsSrpIfc), (*essentialsViewsSitMapIfc));
|
||||
init(m_ui->widget);
|
||||
|
||||
m_ui->dteStartTime->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
|
||||
m_ui->dteEndTime->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
|
||||
|
||||
connect(m_ui->dteStartTime, &LtsWidgets::LtsDateTimeEdit::dateTimeChanged,
|
||||
this, &SituationReplayEditor::onChangesMade);
|
||||
|
||||
connect(m_ui->dteEndTime, &LtsWidgets::LtsDateTimeEdit::dateTimeChanged,
|
||||
this, &SituationReplayEditor::onChangesMade);
|
||||
|
||||
connect(m_ui->sbSpeed, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
|
||||
this, &SituationReplayEditor::onChangesMade);
|
||||
|
||||
|
||||
|
||||
if (m_markerModel != nullptr) // mrk plugin available
|
||||
{
|
||||
m_markerModel->setParent(this); // model was created without parent!
|
||||
m_ui->markersDataView->setModel(m_markerModel);
|
||||
|
||||
m_ui->markersDataView->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColImage);
|
||||
m_ui->markersDataView->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColFileId);
|
||||
m_ui->markersDataView->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColOffset);
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->markersDataView->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColStreams);
|
||||
#endif
|
||||
m_ui->markersDataView->setInitialSearchContent(
|
||||
MarkerManagement::MarkerManagementDialogModel::ColOperator, LtsSessionData::LtsSessionData::getInstance()->getOperatorId()
|
||||
);
|
||||
|
||||
connect(m_ui->markersDataView, &LtsDataView::LtsDataView::activated,
|
||||
this, &SituationReplayEditor::onMarkerSelected);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->markersDataView->hide(); // no markers, no view
|
||||
m_ui->lblMrk->hide();
|
||||
}
|
||||
|
||||
// check if the new replay control widget editor can be used
|
||||
if ((*m_essentialsViewsSrpIfc) != nullptr)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "essentials view available - using srp replay control widget as editor!";
|
||||
onAddReplayControlWidget();
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "essentials view not available - using traditional editor";
|
||||
}
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Script_Editor-Add_Situation_Replay_Topic"));
|
||||
}
|
||||
|
||||
SituationReplayEditor::~SituationReplayEditor()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE
|
||||
|
||||
void SituationReplayEditor::updateInternalData()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION;
|
||||
|
||||
if (m_firstUpdate)
|
||||
{
|
||||
m_firstUpdate = false;
|
||||
retranslate();
|
||||
|
||||
// And again a workaround:
|
||||
// NLDEVCTRL-1434 Filtereinstellung werden nicht übernommen.
|
||||
// Bei Erstellen diese SituationReplayEditor werden die TopicDaten übermitteltet:
|
||||
// SetTopicData ruft dann (indirekt) updateInternalData auf.
|
||||
// Dabei werden dann fälschlichwerweise die _leeren_ m_filterSettings&m_currentSettings
|
||||
// genutzt, um m_topicData zu überschreiben...
|
||||
// Also müssen wir vorher m_filterSettings&m_currentSettings auf Stand bringen:
|
||||
//
|
||||
// apply_2 will be called via updateInternalLength.
|
||||
// Within that at least m_filterSettings and m_currentSettings will be
|
||||
// transfered to m_topic_data....
|
||||
m_filterSettings = m_topicData.properties.value(QStringLiteral("filters"));
|
||||
m_currentSettings = m_topicData.properties.value(QStringLiteral("settings"));
|
||||
|
||||
|
||||
// load & show settings / filters not available for replay, as that topic doesn't store nor apply settings / filters
|
||||
if (m_topicData.topicType == TopicEditorBase::TopicType::Situation)
|
||||
{
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->show();
|
||||
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->show();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTopicDataProperties())
|
||||
return;
|
||||
|
||||
// is positioned (cannot be positioned without data)!
|
||||
qint64 start = getTopicDataValue(QStringLiteral("start")).toDouble();
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(start);
|
||||
double speed = getTopicDataValue(QStringLiteral("speed")).toDouble(1.0);
|
||||
m_ui->sbSpeed->setValue(speed);
|
||||
updateInternalLength();
|
||||
|
||||
if (m_replayControlWidget)
|
||||
{
|
||||
m_replayControlWidget->setStartTime(start);
|
||||
}
|
||||
}
|
||||
|
||||
void SituationReplayEditor::updateInternalLength()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION;
|
||||
qint64 end = calculateEnd(m_ui->dteStartTime->msecsSinceEpoch(), m_topicData.duration, m_ui->sbSpeed->value(), true);
|
||||
bool hasChanges = hasUnsavedChanges(); // has to be done here, as LtsDateTimeEdits cannot be signal-blocked, for whatever reason
|
||||
m_ui->dteEndTime->setMsecsSinceEpoch(end);
|
||||
m_ui->lblDuration->setText(tr("%1 sec").arg(m_topicData.duration));
|
||||
if (!hasChanges) // only save new end time if the user didn't change anything - otherwise his changes will be also applied..
|
||||
apply2();
|
||||
}
|
||||
|
||||
void SituationReplayEditor::retranslate()
|
||||
{
|
||||
if (m_topicData.id == -1)
|
||||
translate(m_ui, tr("Topic Editor"));
|
||||
else
|
||||
{
|
||||
if (m_topicData.topicType == TopicEditorBase::TopicType::Situation)
|
||||
{
|
||||
translate(m_ui, tr("Situation Topic Editor"));
|
||||
m_ui->cbFilterSettings->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
translate(m_ui, tr("Replay Topic Editor"));
|
||||
m_ui->cbFilterSettings->hide();
|
||||
}
|
||||
}
|
||||
|
||||
m_uiRequirements->clear();
|
||||
m_uiRequirements->addRequirement({ m_ui->lbxEndTime, [this]() {
|
||||
return (m_ui->dteEndTime->msecsSinceEpoch() > m_ui->dteStartTime->msecsSinceEpoch());
|
||||
}, tr("End time must be greater than the start time") });
|
||||
m_uiRequirements->addRequirement({ m_ui->lbxStartTime, [this]() {
|
||||
return (m_ui->dteStartTime->msecsSinceEpoch() < m_ui->dteEndTime->msecsSinceEpoch());
|
||||
}, tr("Start time must be smaller than the end time") });
|
||||
}
|
||||
|
||||
|
||||
qint64 SituationReplayEditor::calculateEnd(qint64 startTime, int lengthInSeconds, double speed, bool backwards)
|
||||
{
|
||||
double result = (lengthInSeconds * 1000);
|
||||
if (backwards)
|
||||
result *= speed;
|
||||
else
|
||||
result /= speed;
|
||||
result += startTime;
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " result = " << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
qint64 SituationReplayEditor::calculateDuration(qint64 startTime, qint64 endTime, double speed)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " startTime=" << startTime << " endTime=" << endTime << " speed=" << speed;
|
||||
qint64 duration = endTime - startTime;
|
||||
double result = duration;
|
||||
result /= speed;
|
||||
qint64 result2 = result; // clear
|
||||
result2 += 500; // ceil (ms)
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " result = " << result2;
|
||||
return result2;
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayEditor::setFilterAndSettings(const QByteArray &mapFilter, const QByteArray &mapSettings)
|
||||
{
|
||||
setMapFilter(mapFilter);
|
||||
setMapSettings(mapSettings);
|
||||
}
|
||||
|
||||
void SituationReplayEditor::setMapSettings(const QByteArray &mapSettings)
|
||||
{
|
||||
QJsonParseError parseError;
|
||||
QJsonValue settings = QJsonDocument::fromJson(mapSettings, &parseError).object();
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "map settings not valid json at" << parseError.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentSettings = settings;
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void SituationReplayEditor::setMapFilter(const QByteArray &mapFilter)
|
||||
{
|
||||
QJsonParseError parseError;
|
||||
QJsonValue filter = QJsonDocument::fromJson(mapFilter, &parseError).object();
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "filter settings not valid json at" << parseError.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
m_filterSettings = filter;
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC SLOTS
|
||||
|
||||
void SituationReplayEditor::onEssentialsViewChanged(SituationReplay::ForeignGuiInterface *essentialsViewsSrpIfc,
|
||||
SituationMap::ForeignGuiInterface *essentialsViewsSitMapIfc)
|
||||
{
|
||||
*m_essentialsViewsSrpIfc = essentialsViewsSrpIfc;
|
||||
*m_essentialsViewsSitMapIfc = essentialsViewsSitMapIfc;
|
||||
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Show, !!(*m_essentialsViewsSrpIfc));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE SLOTS
|
||||
|
||||
//bool SituationReplayEditor::apply2()
|
||||
//{
|
||||
// INFO_LOG(m_logger) << "apply2";
|
||||
// qint64 start = m_ui->dteStartTime->msecsSinceEpoch();
|
||||
// qint64 end = m_ui->dteEndTime->msecsSinceEpoch();
|
||||
// double speed = m_ui->sbSpeed->value();
|
||||
|
||||
// qint64 newLength = calcDuration(start, end, speed) / 1000; // as the TrackEntry cannot be larger than the actual replay content / time
|
||||
// m_trackEntry->m_length = newLength;
|
||||
|
||||
// saveTopicData(QStringLiteral("start"), start);
|
||||
// saveTopicData(QStringLiteral("speed"), speed);
|
||||
// saveTopicData(QStringLiteral("end"), end);
|
||||
|
||||
// if (m_ui->cbFilterSettings->isChecked() && m_ui->cbFilterSettings->isVisible())
|
||||
// {
|
||||
// NOTICE_LOG(m_logger) << "m_filterSettings";
|
||||
// saveTopicData(QStringLiteral("filters"), m_filterSettings);
|
||||
// saveTopicData(QStringLiteral("settings"), m_currentSettings);
|
||||
// }
|
||||
|
||||
|
||||
// slotSomethingChanged();
|
||||
|
||||
// this->__unchange__();
|
||||
// applied();
|
||||
|
||||
// return true;
|
||||
//}
|
||||
|
||||
//* Original
|
||||
bool SituationReplayEditor::apply2()
|
||||
{
|
||||
qint64 start = m_ui->dteStartTime->msecsSinceEpoch();
|
||||
qint64 end = m_ui->dteEndTime->msecsSinceEpoch();
|
||||
double speed = m_ui->sbSpeed->value();
|
||||
qint64 duration = calculateDuration(start, end, speed) / 1000; // as the TrackEntry cannot be larger than the actual replay content / time
|
||||
|
||||
setDescription(m_ui->widget->description());
|
||||
setTopicDataProperty(QStringLiteral("start"), start);
|
||||
setTopicDataProperty(QStringLiteral("speed"), speed);
|
||||
setTopicDataProperty(QStringLiteral("end"), end);
|
||||
|
||||
if (m_topicData.channel.isEmpty()) // not set
|
||||
{
|
||||
m_ui->widget->setEnabled(true);
|
||||
m_topicDuration = duration;
|
||||
m_topicData.duration = duration;
|
||||
if (duration < 0)
|
||||
m_ui->lblDuration->setText(QStringLiteral(" - "));
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " setText length = " << duration;
|
||||
m_ui->lblDuration->setText(tr("%1 sec").arg(m_topicDuration));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ui->cbFilterSettings->isChecked() && m_ui->cbFilterSettings->isVisible())
|
||||
{
|
||||
setTopicDataProperty(QStringLiteral("filters"), m_filterSettings);
|
||||
setTopicDataProperty(QStringLiteral("settings"), m_currentSettings);
|
||||
}
|
||||
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
|
||||
if (m_topicData.channel.isEmpty()) // not set
|
||||
return true;
|
||||
|
||||
LtsUtils::Promise *promise = new LtsUtils::Promise(nullptr, this); // empty promise, function set & executed by invoked slot
|
||||
promise->resolved([this, duration](const QVariant &)
|
||||
{
|
||||
m_topicData.duration = duration;
|
||||
this->__unchange__();
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
applied();
|
||||
});
|
||||
promise->rejected([this](const QVariant &)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(
|
||||
this,
|
||||
tr("The new settings conflict with other topics in this script. <br>"
|
||||
"Maybe the new topic size would collide with another topic or would be "
|
||||
"parallel to a topic of a type that cannot be parallelized with this topic's type. <br>"
|
||||
"You might have changed the size by setting a bigger end time or decreased the "
|
||||
"replay speed. <br>"
|
||||
"To fix this issue, check if your topic has enough room to expand if you "
|
||||
"make changed that would resize it."),
|
||||
tr("Unable to apply changes")); // ay caramba
|
||||
});
|
||||
emit durationCheckRequested(duration, promise);
|
||||
return false; // did not fail, but the promise is async. If the promise resolves, the effect of "return true" is executed => __unchange__ & emit signalTrackEntryChanged
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayEditor::onAddReplayControlWidget()
|
||||
{
|
||||
if (m_replayControlWidget)
|
||||
m_replayControlWidget->deleteLater();
|
||||
|
||||
m_replayControlWidget = qobject_cast<SituationReplayControlWidget *>((*m_essentialsViewsSrpIfc)->createControlWidget(true));
|
||||
|
||||
if (m_replayControlWidget == nullptr)
|
||||
return;
|
||||
|
||||
m_replayControlWidget->setStartTime(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
|
||||
connect(m_replayControlWidget, &QObject::destroyed,
|
||||
m_ui->traditionalControlWidget, &QWidget::show,
|
||||
Qt::QueuedConnection /* otherwise race condition !!! */);
|
||||
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::signalSetStart, this, [this](qint64 start)
|
||||
{
|
||||
if (m_ui->dteStartTime->msecsSinceEpoch() != start)
|
||||
{
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(start);
|
||||
onChangesMade();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::signalSetEnd, this, [this](qint64 end)
|
||||
{
|
||||
if (m_ui->dteEndTime->msecsSinceEpoch() != end)
|
||||
{
|
||||
m_ui->dteEndTime->setMsecsSinceEpoch(end);
|
||||
onChangesMade();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::signalSetSpeed, this, [this](double speed)
|
||||
{
|
||||
if (m_ui->sbSpeed->value() != speed)
|
||||
{
|
||||
m_ui->sbSpeed->setValue(speed);
|
||||
onChangesMade();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_ui->sbSpeed, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [this](double speed)
|
||||
{
|
||||
speed *= 10;
|
||||
if (m_replayControlWidget->speed() != speed)
|
||||
{
|
||||
m_replayControlWidget->setSpeed(speed);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::signalSetMapSettings,
|
||||
this, &SituationReplayEditor::setMapSettings);
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::signalSetMapFilter,
|
||||
this, &SituationReplayEditor::setMapFilter);
|
||||
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::replayStarted,
|
||||
this, &SituationReplayEditor::onReplayStarted);
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::replayPaused,
|
||||
this, &SituationReplayEditor::onReplayPaused);
|
||||
connect(m_replayControlWidget, &SituationReplayControlWidget::replayStopped,
|
||||
this, &SituationReplayEditor::onReplayStopped);
|
||||
|
||||
m_replayControlWidget->showEditorWidget();
|
||||
m_ui->traditionalControlWidget->hide();
|
||||
m_ui->verticalLayout_2->addWidget(m_replayControlWidget);
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayEditor::onMarkerSelected(const QModelIndex &idx)
|
||||
{
|
||||
const MarkerManagement::Marker marker = m_markerModel->getRowData(idx.row());
|
||||
|
||||
if (marker.id() <= 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "marker has id 0";
|
||||
return;
|
||||
}
|
||||
|
||||
// qint64 startTime = marker.timeStamp() - marker.timeStampOffset();
|
||||
// m_ui->dteStartTime->setMsecsSinceEpoch(startTime);
|
||||
// m_ui->dteEndTime->setMsecsSinceEpoch(marker.timeStamp() + (10 * 1000)/* TODO: default length? */);
|
||||
onChangesMade();
|
||||
setFilterAndSettings(QByteArray(marker.filter().c_str(), marker.filter().size()), QByteArray(marker.mapSettings().c_str(), marker.mapSettings().size()));
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onChangesMade()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION;
|
||||
|
||||
qint64 start = m_ui->dteStartTime->msecsSinceEpoch();
|
||||
qint64 end = m_ui->dteEndTime->msecsSinceEpoch();
|
||||
double speed = m_ui->sbSpeed->value();
|
||||
qint64 duration = calculateDuration(start, end, speed) / 1000;
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " duration = " << duration;
|
||||
|
||||
if (duration < 0)
|
||||
m_ui->lblDuration->setText(QStringLiteral(" - "));
|
||||
else
|
||||
{
|
||||
m_topicDuration = duration;
|
||||
m_topicData.duration = duration;
|
||||
m_ui->widget->previewer()->setTopicData(m_topicData);
|
||||
m_ui->lblDuration->setText(tr("%1 sec").arg(m_topicDuration));
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " setText duration = " << duration;
|
||||
}
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onPreview()
|
||||
{
|
||||
if (m_ui->dteStartTime->msecsSinceEpoch() >= m_ui->dteEndTime->msecsSinceEpoch())
|
||||
{
|
||||
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Start time must be greater than end time"));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*m_essentialsViewsSrpIfc) == nullptr)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Preview could not be started because essentials view is not started."));
|
||||
return;
|
||||
}
|
||||
|
||||
(*m_essentialsViewsSrpIfc)->startReplay(
|
||||
m_ui->dteStartTime->msecsSinceEpoch(), m_ui->dteEndTime->msecsSinceEpoch(), m_ui->sbSpeed->value()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayEditor::onReplayStarted()
|
||||
{
|
||||
if (m_topicData.id != -1)
|
||||
m_ui->widget->setTopicEnabled(false);
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onReplayPaused()
|
||||
{
|
||||
if (m_topicData.id != -1)
|
||||
m_ui->widget->setTopicEnabled(true);
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onReplayStopped()
|
||||
{
|
||||
if (m_topicData.id != -1)
|
||||
m_ui->widget->setTopicEnabled(true);
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onShowMapSettings()
|
||||
{
|
||||
if (m_currentSettings.isNull() && m_filterSettings.isNull())
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("No saved map and filter settings available to show"));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*m_essentialsViewsSitMapIfc) == nullptr)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Settings could not be shown because essentials view was closed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_currentSettings.isNull())
|
||||
{
|
||||
if (!(*m_essentialsViewsSitMapIfc)->setOperatorMapSettings(m_currentSettings))
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("This topic has invalid map settings"), tr("Could not set map settings from topic"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_filterSettings.isNull())
|
||||
{
|
||||
if (m_applyFilter != nullptr)
|
||||
m_applyFilter->applyFilter(m_filterSettings.toObject());
|
||||
else
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Filter settings could not be set because essentials view was closed"));
|
||||
}
|
||||
}
|
||||
|
||||
void SituationReplayEditor::onGetSettings()
|
||||
{
|
||||
if ((*m_essentialsViewsSitMapIfc) == nullptr)
|
||||
{
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Map settings could not be received because essentials view was closed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_applyFilter == nullptr)
|
||||
{
|
||||
NOTICE_LOG(m_logger) << "m_applyFilter == nullptr";
|
||||
new LtsWidgets::LtsMessageDialogNew(this, tr("Filter settings could not be received because essentials view was closed"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentSettings = (*m_essentialsViewsSitMapIfc)->operatorMapSettings();
|
||||
m_filterSettings = m_applyFilter->getFilterParamsJson();
|
||||
//m_ui->markerView->selectionModel()->clear(); TODO...?
|
||||
slotChangesMade();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace SituationMap
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Topic Editor for the Situation Replay in the Script Editor
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*
|
||||
* Created by Mike Wille, SLSE, 2017-04-04
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Plugins/MarkerManagement/Gui/Common/mrk_dialog_model.h>
|
||||
#include <Plugins/MarkerManagement/Gui/Common/mrk_apply_filter.h>
|
||||
#include <Services/MarkerManagement/mrk.hpp>
|
||||
#include <TopicEditorBase/topic_preview_widget.h>
|
||||
#include <TopicEditorBase/topic_previewer.h>
|
||||
|
||||
#include <TopicEditorBase/topic_editor_base.h>
|
||||
|
||||
|
||||
namespace LtsSession { class LtsSession; }
|
||||
namespace MarkerManagement { class MarkerManagementDialogModel; }
|
||||
namespace NGL { class INGLLogger; }
|
||||
namespace Ui { class SituationReplayEditor; }
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
|
||||
class ForeignGuiInterface;
|
||||
class SituationReplayControlWidget;
|
||||
|
||||
|
||||
class SituationReplayEditor : public TopicEditorBase::TopicEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SituationReplayEditor(MarkerManagement::MarkerManagementDialogModel *markerModel,
|
||||
const QPointer<MarkerManagement::MarkerManagementApplyFilter> &applyFilter,
|
||||
LtsSession::LtsSession *session, QWidget *parent/* = nullptr*/,
|
||||
SituationReplay::ForeignGuiInterface **essentialsViewsSrpIfc,
|
||||
SituationMap::ForeignGuiInterface **essentialsViewsSitMapIfc
|
||||
);
|
||||
~SituationReplayEditor();
|
||||
|
||||
private:
|
||||
void updateInternalData() override;
|
||||
void updateInternalLength() override;
|
||||
void retranslate() override;
|
||||
|
||||
qint64 calculateEnd(qint64 startTime, int lengthInSeconds, double speed, bool backwards = false);
|
||||
qint64 calculateDuration(qint64 startTime, qint64 endTime, double speed);
|
||||
|
||||
void setFilterAndSettings(const QByteArray &mapFilter, const QByteArray &mapSettings);
|
||||
void setMapSettings(const QByteArray &mapSettings);
|
||||
void setMapFilter(const QByteArray &mapFilter);
|
||||
|
||||
public slots:
|
||||
void onEssentialsViewChanged(SituationReplay::ForeignGuiInterface *essentialsViewsSrpIfc, SituationMap::ForeignGuiInterface *essentialsViewsSitMapIfc);
|
||||
|
||||
private slots:
|
||||
bool apply2() override final;
|
||||
|
||||
void onAddReplayControlWidget();
|
||||
void onMarkerSelected(const QModelIndex &idx);
|
||||
void onChangesMade();
|
||||
void onPreview();
|
||||
|
||||
void onReplayStarted();
|
||||
void onReplayPaused();
|
||||
void onReplayStopped();
|
||||
void onShowMapSettings();
|
||||
void onGetSettings();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
LtsSession::LtsSession *m_session{nullptr};
|
||||
SituationReplay::ForeignGuiInterface **m_essentialsViewsSrpIfc;
|
||||
SituationMap::ForeignGuiInterface **m_essentialsViewsSitMapIfc { nullptr };
|
||||
SituationReplayControlWidget *m_replayControlWidget{nullptr};
|
||||
|
||||
MarkerManagement::MarkerManagementDialogModel *m_markerModel{nullptr};
|
||||
QPointer<MarkerManagement::MarkerManagementApplyFilter> m_applyFilter;
|
||||
QJsonValue m_currentSettings;
|
||||
QJsonValue m_filterSettings;
|
||||
bool m_firstUpdate{true};
|
||||
|
||||
Ui::SituationReplayEditor *m_ui{nullptr};
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
};
|
||||
|
||||
} // namespace SituationMap
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SituationReplayEditor</class>
|
||||
<widget class="QWidget" name="SituationReplayEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>785</width>
|
||||
<height>540</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Script Editor</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbxStartTime">
|
||||
<property name="text">
|
||||
<string>Start:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="LtsWidgets::LtsDateTimeEdit" name="dteStartTime">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbxEndTime">
|
||||
<property name="text">
|
||||
<string>End:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="LtsWidgets::LtsDateTimeEdit" name="dteEndTime">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lbxSpeed">
|
||||
<property name="text">
|
||||
<string>Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="sbSpeed">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string notr="true">x</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>16.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lbxDuration">
|
||||
<property name="text">
|
||||
<string>Duration:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="lblDuration">
|
||||
<property name="text">
|
||||
<string notr="true">%1 sec</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="cbFilterSettings">
|
||||
<property name="text">
|
||||
<string>Include Filter Settings</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="TopicEditorBase::TopicPreviewWidget" name="widget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="traditionalControlWidget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string extracomment="Um einen detailierteren Editor mit Direkt-Abspiel Möglichkeit zu erhalten, starten Sie zuerst die Hilfsansicht und öffnen dann diesen Dialog erneut.">(To obtain a more detailed editor with direct replay controls, open the essentials view, then reopen this dialog.)</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblMrk">
|
||||
<property name="text">
|
||||
<string>Set Start & End by markers:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LtsDataView::LtsDataView" name="markersDataView">
|
||||
<property name="showColumnCount" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonsVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsWidgets::LtsDateTimeEdit</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsWidgets/lts_date_time_edit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LtsDataView::LtsDataView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsDataView/lts_data_view.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TopicEditorBase::TopicPreviewWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>TopicEditorBase/topic_preview_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LtsDialog container for the exportable widget with replay controls
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Mike Wille, NLE, 2019-10-02
|
||||
*/
|
||||
|
||||
#include "replay_control_dialog.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
|
||||
SituationReplayControlDialog::SituationReplayControlDialog(SituationReplayControlWidget *replayWidget, QWidget *parent)
|
||||
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
|
||||
, m_logger(getLogger("ReplayControlDialog"))
|
||||
{
|
||||
auto l = new QHBoxLayout;
|
||||
setLayout(l);
|
||||
|
||||
l->addWidget(replayWidget);
|
||||
replayWidget->show();
|
||||
|
||||
setOnlineHelpFilename(QStringLiteral("Situation-Start_Situation_Playback"));
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlDialog::retranslate()
|
||||
{
|
||||
// lupdate doesn't like translate(tr("foo")) ...
|
||||
const QString txt(tr("Replay Control"));
|
||||
translate(txt);
|
||||
}
|
||||
|
||||
} // namespace SituationReplay
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LtsDialog container for the exportable widget with replay controls
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Mike Wille, NLE, 2019-10-02
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "replay_control_widget.h"
|
||||
|
||||
#include <SituationReplay/logger.h>
|
||||
#include <LtsWidgets/lts_dialog.h>
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
class SituationReplayControlDialog final : public LtsWidgets::LtsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SituationReplayControlDialog(SituationReplayControlWidget *replayWidget, QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void retranslate() override;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace SituationReplay
|
||||
+454
@@ -0,0 +1,454 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* GUI Plugin for SituationReplay - Replay Control
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwar, SLSE, 2016-07-19
|
||||
*/
|
||||
|
||||
#include "replay_control_gui_plugin.h"
|
||||
#include "replay_control_widget.h"
|
||||
#include "replay_control_dialog.h"
|
||||
#include "TopicEditor/topic_editor.h"
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsSession/lts_session_data.h>
|
||||
#include <SituationReplay/srp_operator_rights.h>
|
||||
#include <PresentationManagement/Gui/Script/Common/TakeHomePackageGenerator/export.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_script_filter_gui_plugin_interface.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
||||
{
|
||||
return std::make_shared<InterfaceImpl>(session);
|
||||
}
|
||||
|
||||
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
||||
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Srp.ReplayControl"))
|
||||
, m_dtc(nullptr)
|
||||
, m_ftc(nullptr)
|
||||
, m_mrkDtc(nullptr)
|
||||
, m_playbackStartTime(-1)
|
||||
, m_playbackEndTime(-1)
|
||||
, m_playbackSpeed(-1)
|
||||
, m_replayWidget(nullptr)
|
||||
, m_sitMapIfc(nullptr)
|
||||
{}
|
||||
|
||||
|
||||
bool InterfaceImpl::fetchInterfaces()
|
||||
{
|
||||
if (!m_session->getDtc<DataInterface>(m_dtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "No Srp Dtc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
||||
<< " partition " << m_session->partitionId()
|
||||
<< " type " << m_session->partitionType();
|
||||
return false;
|
||||
}
|
||||
if (!m_session->getFtc<DataInterface>(m_ftc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "No Srp Ftc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
||||
<< " partition " << m_session->partitionId()
|
||||
<< " type " << m_session->partitionType();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional:
|
||||
if (!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "(Optional) No Mrk Data Ifc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
||||
<< " partition " << m_session->partitionId()
|
||||
<< " type " << m_session->partitionType();
|
||||
}
|
||||
|
||||
|
||||
LtsSession::Fetcher *fetcher = m_session->fetcher(QStringLiteral("Situation Replay Control"));
|
||||
|
||||
fetcher->getForeignGuiInterface(m_mrkIfc, QStringLiteral("marker selection in replay control"), true);
|
||||
|
||||
// if (!m_session->getForeignGuiInterface(m_mrkIfc))
|
||||
// {
|
||||
// WARNING_LOG(m_logger) << "(Optional) No Mrk Gui Ifc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
||||
// << " partition " << m_session->partitionId()
|
||||
// << " type " << m_session->partitionType();
|
||||
// }
|
||||
|
||||
if (!m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc))
|
||||
WARNING_LOG(m_logger) << "No Map Gui Ifc: " << m_session->toString();
|
||||
|
||||
return fetcher->status();
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::clearInterfaces()
|
||||
{
|
||||
if (m_replayWidget != nullptr)
|
||||
m_replayWidget->deleteLater();
|
||||
|
||||
m_mrkDtc = nullptr;
|
||||
m_ftc = nullptr;
|
||||
m_dtc = nullptr;
|
||||
m_sitMapGuiIfc = nullptr;
|
||||
m_mrkIfc = nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::startReplay(const qint64 start, const qint64 end, const double speed, const QJsonObject &attributes)
|
||||
{
|
||||
const QJsonValue mapConfig = attributes[QStringLiteral("settings")];
|
||||
if (mapConfig.isNull())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Note: Situation Map Script Filter settings is empty";
|
||||
}
|
||||
|
||||
createReplayDialogIfNeeded();
|
||||
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "start = " << start << ", end = " << end << ", speed = " << speed << ", attributes: " << QString::fromUtf8(QJsonDocument(attributes).toJson());
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "state = " << m_replayWidget->getReplayState() << ", replay id = " << m_replayWidget->getId();
|
||||
|
||||
// always stop & play if start was called, otherwise new start/end/speed values are dismissed
|
||||
// if (m_replayDialog->getReplayState() == SrpState_def::Paused)
|
||||
// m_replayDialog->resumeReplay();
|
||||
// else
|
||||
return m_replayWidget->startReplay(start, end, speed);
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::pauseReplay()
|
||||
{
|
||||
createReplayDialogIfNeeded(false);
|
||||
m_replayWidget->pauseReplay();
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::stopReplay()
|
||||
{
|
||||
createReplayDialogIfNeeded(false);
|
||||
m_replayWidget->stopReplay();
|
||||
}
|
||||
|
||||
|
||||
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
||||
{
|
||||
QVector<LtsWidgets::PluginData> ret;
|
||||
if (m_session->partitionType() != IDL::PartitionType::Replay)
|
||||
return ret;
|
||||
|
||||
LtsWidgets::PluginData plData;
|
||||
|
||||
plData.menuGroup = menuGroup();
|
||||
|
||||
LtsSessionData::LtsSessionData *sd = LtsSessionData::LtsSessionData::getInstance();
|
||||
|
||||
if (sd->debugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl")) == QVariant(true) /* HACK */ &&
|
||||
(!sd->hasDebugFlag(QStringLiteral("DoNotCreateInstant.ReplayControl.Ignore")) || sd->debugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl.Ignore")) == QVariant(false)))
|
||||
sd->setDebugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl"), false); // HACK: ! - set 'back' to false
|
||||
else
|
||||
plData.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndAddAction;
|
||||
|
||||
plData.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
|
||||
plData.menuText = tr("Playback Control");
|
||||
plData.identifier = QLatin1String("PlaybackControlDialog");
|
||||
plData.menuIcon = QIcon(QLatin1String("icon:load.svg"));
|
||||
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
|
||||
plData.createWidgetFunc = [this](QWidget *parent)
|
||||
{
|
||||
return onShowReplayControlDialog(parent);
|
||||
};
|
||||
ret.append(plData);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
QString InterfaceImpl::menuGroup() const
|
||||
{
|
||||
return tr("Playbacks");
|
||||
}
|
||||
|
||||
|
||||
// private
|
||||
void InterfaceImpl::createReplayDialogIfNeeded(bool useExistingReplay)
|
||||
{
|
||||
if (m_replayWidget != nullptr)
|
||||
return;
|
||||
|
||||
m_replayWidget = qobject_cast<SituationReplayControlWidget *>(createControlWidget(useExistingReplay));
|
||||
}
|
||||
|
||||
|
||||
QWidget *InterfaceImpl::createControlWidget(bool useExistingReplay)
|
||||
{
|
||||
auto situationReplayControlWidget = new SituationReplayControlWidget;
|
||||
bool r = situationReplayControlWidget->init(m_session, m_dtc, m_ftc, m_mrkDtc, m_sitMapGuiIfc, m_session->partitionId(), useExistingReplay);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
situationReplayControlWidget->deleteLater();
|
||||
situationReplayControlWidget = nullptr;
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// m_replayWidgetList.append(situationReplayControlWidget);
|
||||
// connect(situationReplayControlWidget, &QObject::destroyed,
|
||||
// this, [this, situationReplayControlWidget]()
|
||||
// {
|
||||
// m_replayWidgetList.removeAll(situationReplayControlWidget);
|
||||
// });
|
||||
// }
|
||||
|
||||
return situationReplayControlWidget;
|
||||
}
|
||||
|
||||
|
||||
LtsWidgets::LtsDialog *InterfaceImpl::onShowReplayControlDialog(QWidget *)
|
||||
{
|
||||
// NOTE: One Instance of this dialog for a session as this IS a replay session. Exactly one session.
|
||||
createReplayDialogIfNeeded();
|
||||
return new SituationReplayControlDialog(m_replayWidget);
|
||||
}
|
||||
|
||||
|
||||
TopicEditorBase::TopicEditor *InterfaceImpl::createTopicEditor(const TopicEditorBase::TopicData &data)
|
||||
{
|
||||
MarkerManagement::MarkerManagementDialogModel *markerModel = nullptr;
|
||||
|
||||
if (m_mrkIfc)
|
||||
markerModel = m_mrkIfc->createMarkerModel();
|
||||
else
|
||||
WARNING_LOG(m_logger) << "No Mrk Ifc for SitRep Topic Editor";
|
||||
|
||||
if (m_essentialsViewSrpIfc == nullptr)
|
||||
WARNING_LOG(m_logger) << "essentials view's srp ifc not available - no preview available";
|
||||
if (m_applyFilter == nullptr)
|
||||
WARNING_LOG(m_logger) << "m_applyFilter == nullptr";
|
||||
SituationReplayEditor *editor = new SituationReplayEditor(markerModel, m_applyFilter,
|
||||
m_session, nullptr, &m_essentialsViewSrpIfc, &m_essentialsViewSitMapIfc);
|
||||
QObject::connect(this, &InterfaceImpl::signalEssentialsViewChanged, editor, &SituationReplayEditor::onEssentialsViewChanged);
|
||||
editor->setTopicData(data);
|
||||
return editor;
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::setSitMapIfc(SituationMap::ForeignGuiInterface *sitMapIfc)
|
||||
{
|
||||
m_sitMapIfc = sitMapIfc;
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::setEssentialsViewInterfaces(ForeignGuiInterface *srpIfc, SituationMap::ForeignGuiInterface *sitMapIfc)
|
||||
{
|
||||
m_essentialsViewSrpIfc = srpIfc;
|
||||
m_essentialsViewSitMapIfc = sitMapIfc;
|
||||
emit signalEssentialsViewChanged(m_essentialsViewSrpIfc, m_essentialsViewSitMapIfc);
|
||||
}
|
||||
|
||||
|
||||
void InterfaceImpl::setApplyFilter(const QPointer<MarkerManagement::MarkerManagementApplyFilter> &applyFilter)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "setApplyFilter";
|
||||
m_applyFilter = applyFilter;
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::playbackInit(const QJsonObject& attributes)
|
||||
{
|
||||
QJsonDocument doc(attributes);
|
||||
DEBUG_LOG(m_logger) << "attributes=" << doc.toJson(QJsonDocument::Compact);
|
||||
|
||||
// load map settings (if any available)
|
||||
const QJsonValue mapConfig = attributes[QStringLiteral("settings")];
|
||||
|
||||
if (!mapConfig.isNull()) // maybe a pure replay topic, not a situation topic which contains map settings, etc.
|
||||
{
|
||||
//m_oldMapData = m_sitMapIfc->operatorMapSettings();
|
||||
m_sitMapIfc->setOperatorMapSettings(mapConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Note: map settings is empty";
|
||||
}
|
||||
|
||||
// load map filters (if any available)
|
||||
const QJsonObject mapFilters = attributes[QStringLiteral("filters")].toObject();
|
||||
|
||||
if (m_applyFilter != nullptr && !mapFilters.isEmpty())
|
||||
{
|
||||
//m_oldFilterData = m_applyFilter->getFilterParamsJson();
|
||||
m_applyFilter->applyFilter(mapFilters);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_applyFilter == nullptr)
|
||||
WARNING_LOG(m_logger) << "ApplyFilter missing for ReplayControl TopicReplayInterface";
|
||||
else if (mapFilters.isEmpty())
|
||||
WARNING_LOG(m_logger) << "Note: filter settings is empty";
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Replay Control playbackInit";
|
||||
|
||||
m_playbackStartTime = attributes.value(QStringLiteral("start")).toDouble();
|
||||
m_playbackEndTime = attributes.value(QStringLiteral("end")).toDouble();
|
||||
m_playbackSpeed = attributes.value(QStringLiteral("speed")).toDouble();
|
||||
|
||||
bool result = (m_playbackStartTime != 0 && m_playbackEndTime != 0 && m_playbackSpeed != 0);
|
||||
if (!result)
|
||||
WARNING_LOG(m_logger) << "Invalid data for topic replay: start " << m_playbackStartTime
|
||||
<< " end " << m_playbackEndTime
|
||||
<< " speed " << m_playbackSpeed;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::playbackStart(int secondsOffset)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Playback start: m_playbackStartTime = " << m_playbackStartTime << ", offset = " << secondsOffset << ", speed = " << m_playbackSpeed;
|
||||
return startReplay(m_playbackStartTime + ((secondsOffset * m_playbackSpeed) * 1000), 0 /* infinite */, m_playbackSpeed);
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::playbackPause()
|
||||
{
|
||||
pauseReplay();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//void InterfaceImpl::resetMap() <= Do not reset map as map filters are not going to be reversed once set, because the transition from one to another filter would otherwise be very ugly - kind of a hacky workaround
|
||||
//{
|
||||
// if (m_sitMapIfc != nullptr)
|
||||
// {
|
||||
// if (!m_oldMapData.isNull() && !m_oldMapData.isUndefined())
|
||||
// m_sitMapIfc->setOperatorMapSettings(m_oldMapData); // reset map settings
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// WARNING_LOG(m_logger) << "SitMapIfc missing for SitMapScriptFilter TopicReplayInterface";
|
||||
// }
|
||||
|
||||
|
||||
// if (m_applyFilter != nullptr)
|
||||
// {
|
||||
// if (!m_oldFilterData.isNull() && !m_oldFilterData.isUndefined())
|
||||
// m_applyFilter->applyFilter(m_oldFilterData.toObject()); // reset map filters
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// WARNING_LOG(m_logger) << "ApplyFilter missing for ReplayControl TopicReplayInterface";
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
// TopicReplayInterface
|
||||
InterfaceImpl::TopicReplayInterface::TopicReplayInterface(TopicEditorBase::TopicType type, InterfaceImpl *replayControl) // 1:1
|
||||
: m_replayControl(replayControl)
|
||||
, m_type(type)
|
||||
{}
|
||||
|
||||
|
||||
InterfaceImpl::TopicReplayInterface::~TopicReplayInterface()
|
||||
{
|
||||
m_replayControl->stopReplay();
|
||||
//m_replayControl->resetMap();
|
||||
}
|
||||
|
||||
|
||||
// Script Playback Controls:
|
||||
bool InterfaceImpl::TopicReplayInterface::playbackInit(const QJsonObject &attributes)
|
||||
{
|
||||
return m_replayControl->playbackInit(attributes);
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::TopicReplayInterface::playbackStart(int secondsOffset)
|
||||
{
|
||||
return m_replayControl->playbackStart(secondsOffset);
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::TopicReplayInterface::playbackPause()
|
||||
{
|
||||
return m_replayControl->playbackPause();
|
||||
}
|
||||
|
||||
|
||||
bool InterfaceImpl::TopicReplayInterface::exportTopic(const QJsonObject &attributes,
|
||||
const QString &folderPath,
|
||||
TopicEditorBase::ExportNotifier *pExportNotifier)
|
||||
{
|
||||
qint64 playbackStartTime_ms = attributes.value(QStringLiteral("start")).toDouble();
|
||||
qint64 playbackEndTime_ms = attributes.value(QStringLiteral("end")).toDouble();
|
||||
|
||||
// filter?
|
||||
if (playbackStartTime_ms == 0 && playbackEndTime_ms == 0)
|
||||
{
|
||||
pExportNotifier->setExportFailed();
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString attributesFileName = QStringLiteral("sitreplay.json");
|
||||
QFile f(folderPath + attributesFileName);
|
||||
f.open(QIODevice::WriteOnly);
|
||||
f.write(QJsonDocument(attributes).toJson(QJsonDocument::Indented));
|
||||
f.close();
|
||||
|
||||
const QString strFileName = QStringLiteral("sitreplay.sqlite");
|
||||
bool success = TakeHomePackageGenerator::exportSituationMapData(
|
||||
m_replayControl->getSession()->exerciseId(), folderPath + strFileName, playbackStartTime_ms, playbackEndTime_ms
|
||||
);
|
||||
if (success)
|
||||
pExportNotifier->setExportSuccessful(strFileName);
|
||||
else
|
||||
pExportNotifier->setExportFailed();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
QVector<TopicEditorBase::TopicData> InterfaceImpl::TopicReplayInterface::additionalTrackEntries(const QJsonObject &attributes, bool &keepOriginalTopic) const
|
||||
{
|
||||
keepOriginalTopic = true;
|
||||
|
||||
if (m_type != TopicEditorBase::TopicType::Situation) // combined topic, including map filter settings that - filter settings need to be processed by its parent gui's additionalTrackEntries method
|
||||
return {}; // no additional tes if this is a simple replay topic
|
||||
|
||||
keepOriginalTopic = false; // as the situation topic is not a real topic by itself, don't keep it!
|
||||
|
||||
// the 'situation' topic is a 3 in one topic - map, replay, filter - so the other 2 have to be returned here for thp to recognize
|
||||
|
||||
QVector<TopicEditorBase::TopicData> result;
|
||||
|
||||
// first, add the simple map topic:
|
||||
TopicEditorBase::TopicData data;
|
||||
data.topicType = TopicEditorBase::TopicType::SituationMap;
|
||||
result.append(data);
|
||||
|
||||
|
||||
// get processed filter topics
|
||||
std::shared_ptr<SituationMap::ScriptFilterForeignGuiInterface> filterIfc;
|
||||
if (!m_replayControl->m_session->getForeignGuiInterface<SituationMap::ScriptFilterForeignGuiInterface>(filterIfc))
|
||||
{
|
||||
ERROR_LOG(m_replayControl->m_logger) << "sitmap filter ifc missing - map filters for thp will be missing!";
|
||||
return result;
|
||||
}
|
||||
|
||||
QScopedPointer<TopicReplay::Interface> topicIfc(filterIfc->createInterface(TopicEditorBase::TopicType::SituationMapFilter));
|
||||
if (topicIfc == nullptr)
|
||||
{
|
||||
ERROR_LOG(m_replayControl->m_logger) << "sitmap filter topic replay interface cannot be created - map filters will not be available in thp!";
|
||||
return result;
|
||||
}
|
||||
|
||||
result += topicIfc->additionalTrackEntries(attributes);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace SituationReplay
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
*@RDE_HEADER@
|
||||
* Gui Plugin for SituationReplay - Replay Control
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, SLSE, 2016-07-27
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SituationReplay/logger.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <PluginInterfaces/topic_replay_interface.h>
|
||||
#include <Plugins/SituationReplay/srp_data_plugin_interface.h>
|
||||
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
|
||||
#include <Plugins/MarkerManagement/mrk_data_plugin_interface.h>
|
||||
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
|
||||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||||
#include <MarkerManagement/Gui/Common/mrk_apply_filter.h>
|
||||
#include <QPointer>
|
||||
|
||||
|
||||
namespace LtsScriptTimeline { class TopicEditor; }
|
||||
namespace TopicEditorBase { struct TopicData; }
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
|
||||
class SituationReplayControlWidget;
|
||||
|
||||
|
||||
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
|
||||
{
|
||||
Q_PLUGIN_METADATA(IID "gui:3:srp")
|
||||
Q_INTERFACES(GuiPluginBase::Plugin)
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GuiPlugin() = default;
|
||||
~GuiPlugin() = default;
|
||||
|
||||
QString pluginName() override { return QStringLiteral("SituationReplayControl"); }
|
||||
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override
|
||||
{
|
||||
return partition == IDL::PartitionType::Replay || partition == IDL::PartitionType::Exercise;
|
||||
// TODO: why exercise?
|
||||
// needed for no good reason inside InterfaceImpl::transportTopicsToReplaySession() - don't
|
||||
// know why there the plugins must be loaded ...
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class InterfaceImpl final : public QObject, public ForeignGuiInterface, public GuiPluginBase::Interface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void signalEssentialsViewChanged(SituationReplay::ForeignGuiInterface *essentialsViewSrpIfc,
|
||||
SituationMap::ForeignGuiInterface *sitMapIfc);
|
||||
|
||||
public:
|
||||
explicit InterfaceImpl(LtsSession::LtsSession *session);
|
||||
|
||||
QVector<LtsWidgets::PluginData> getActions() override;
|
||||
QString menuGroup() const override;
|
||||
bool fetchInterfaces() override;
|
||||
void clearInterfaces() override;
|
||||
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Replay; }
|
||||
|
||||
bool startReplay(const qint64 start, const qint64 end = 0/*infinite*/, const double speed = 1.0, const QJsonObject &attributes = QJsonObject()) override;
|
||||
void pauseReplay() override;
|
||||
void stopReplay() override;
|
||||
|
||||
// for Topic Editor in Script Editor to start a replay in Essentials View instead of the Script Editor
|
||||
void setSitMapIfc(SituationMap::ForeignGuiInterface *sitMapIfc) override;
|
||||
void setEssentialsViewInterfaces(ForeignGuiInterface *srpIfc, SituationMap::ForeignGuiInterface *sitMapIfc) override;
|
||||
void setApplyFilter(const QPointer<MarkerManagement::MarkerManagementApplyFilter> &applyFilter) override;
|
||||
|
||||
// Play (single instance only)
|
||||
bool playbackInit(const QJsonObject &attributes);
|
||||
bool playbackStart(int secondsOffset);
|
||||
bool playbackPause();
|
||||
//void resetMap();
|
||||
|
||||
// Edit
|
||||
TopicEditorBase::TopicEditor *createTopicEditor(const TopicEditorBase::TopicData &data) override;
|
||||
|
||||
LtsSession::LtsSession* getSession() { return m_session; }
|
||||
QWidget *createControlWidget(bool useExistingReplay) override;
|
||||
|
||||
private:
|
||||
void createReplayDialogIfNeeded(bool useExistingReplay = true);
|
||||
LtsWidgets::LtsDialog *onShowReplayControlDialog(QWidget *parent);
|
||||
|
||||
private:
|
||||
std::shared_ptr<SituationReplay::Interface::Dtc> m_dtc;
|
||||
std::shared_ptr<SituationReplay::Interface::Ftc> m_ftc;
|
||||
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
|
||||
std::shared_ptr<MarkerManagement::ForeignGuiInterface> m_mrkIfc;
|
||||
SituationReplay::ForeignGuiInterface *m_essentialsViewSrpIfc { nullptr };
|
||||
SituationMap::ForeignGuiInterface *m_essentialsViewSitMapIfc { nullptr };
|
||||
std::shared_ptr<SituationMap::ForeignGuiInterface> m_sitMapGuiIfc;
|
||||
|
||||
qint64 m_playbackStartTime;
|
||||
qint64 m_playbackEndTime;
|
||||
double m_playbackSpeed;
|
||||
QPointer<SituationReplayControlWidget> m_replayWidget; // there can only be one dialog!
|
||||
|
||||
QPointer<MarkerManagement::MarkerManagementApplyFilter> m_applyFilter;
|
||||
// QJsonValue m_oldMapData;
|
||||
// QJsonValue m_oldFilterData;
|
||||
SituationMap::ForeignGuiInterface *m_sitMapIfc { nullptr };
|
||||
|
||||
// NOTE: this TRI covers the Situation *and* the SituationReplay topic!
|
||||
class TopicReplayInterface : public TopicReplay::Interface
|
||||
{
|
||||
public:
|
||||
explicit TopicReplayInterface(TopicEditorBase::TopicType type, InterfaceImpl *replayControl);
|
||||
~TopicReplayInterface();
|
||||
|
||||
// Script Playback Controls:
|
||||
bool playbackInit(const QJsonObject &attributes) override;
|
||||
bool playbackStart(int secondsOffset) override;
|
||||
bool playbackPause() override;
|
||||
|
||||
bool exportTopic(const QJsonObject &attributes,
|
||||
const QString &folderPath,
|
||||
TopicEditorBase::ExportNotifier *pExportNotifier) override;
|
||||
QVector<TopicEditorBase::TopicData> additionalTrackEntries(const QJsonObject &attributes, bool &keepOriginalTopic) const override;
|
||||
|
||||
private:
|
||||
InterfaceImpl *m_replayControl;
|
||||
TopicEditorBase::TopicType m_type;
|
||||
};
|
||||
TopicReplay::Interface *createInterface(TopicEditorBase::TopicType type) override
|
||||
{
|
||||
return new TopicReplayInterface(type, this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace SituationReplay
|
||||
@@ -0,0 +1,774 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Exportable widget with replay controls
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Mike Wille, NLE, 2019-10-02
|
||||
*/
|
||||
|
||||
|
||||
#include "replay_control_widget.h"
|
||||
#include "replay_control_dialog.h"
|
||||
#include "ui_replay_control_widget.h"
|
||||
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
#include <OperatorManagement/opm_data_plugin_interface.h>
|
||||
#include <MarkerManagement/Gui/Common/mrk_dialog_model.h>
|
||||
#include <MarkerManagement/Gui/Common/mrk_apply_filter.h>
|
||||
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
|
||||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||||
#include <SituationMap/Map/operator_map_settings.h> // manipulation of OperatorMapSettings
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
|
||||
SituationReplayControlWidget::SituationReplayControlWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::SituationReplayControlWidget)
|
||||
, m_dtc(nullptr)
|
||||
, m_ftc(nullptr)
|
||||
, m_mrkDtc(nullptr) // can be 0 !
|
||||
, m_markerModel(nullptr)
|
||||
, m_sitMapGuiIfc(nullptr) // can be 0 !
|
||||
, m_exmDtc(nullptr)
|
||||
, m_requestHelper(new LtsUtils::RequestHelper(this, true))
|
||||
, m_id(0)
|
||||
, m_partitionId(0)
|
||||
, m_state(SrpState::Unknown)
|
||||
, m_toggleStartPauseButton(true)
|
||||
, m_speedValueChangedTimer(new QTimer(this))
|
||||
, m_logger(getLogger("SituationReplayControlWidget"))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
m_ui->pbStart->setHidden(m_toggleStartPauseButton);
|
||||
m_ui->pbPause->setHidden(m_toggleStartPauseButton);
|
||||
m_ui->pbStartPause->setHidden(!m_toggleStartPauseButton);
|
||||
|
||||
// to avoid enabeling of apply button of lts_dialog
|
||||
m_ui->lblCurrentPartitionTime->setProperty("const", true);
|
||||
m_ui->slPeriodSlider->setProperty("const", true);
|
||||
m_ui->lblStart->setProperty("const", true);
|
||||
m_ui->lblEnd->setProperty("const", true);
|
||||
m_ui->lblSpeedVal->setProperty("const", true);
|
||||
m_ui->slSpeed->setProperty("const", true);
|
||||
m_ui->pbStartPause->setProperty("const", true);
|
||||
m_ui->dteStartTime->setProperty("const", true);
|
||||
m_ui->dteStartTime->setSubWidgetsProperties(QStringLiteral("const"), true);
|
||||
|
||||
connect(m_ui->pbStartPause, &QToolButton::toggled,
|
||||
this, &SituationReplayControlWidget::onStartPauseButtonToggled);
|
||||
|
||||
connect(m_ui->pbStart, &QToolButton::clicked,
|
||||
this, &SituationReplayControlWidget::onStartReplay);
|
||||
connect(m_ui->pbStop, &QToolButton::clicked,
|
||||
this, &SituationReplayControlWidget::onStopReplay);
|
||||
connect(m_ui->pbPause, &QToolButton::clicked,
|
||||
this, &SituationReplayControlWidget::onPauseResumeReplay);
|
||||
|
||||
m_speedValueChangedTimer->setSingleShot(true);
|
||||
connect(m_speedValueChangedTimer, &QTimer::timeout,
|
||||
this, &SituationReplayControlWidget::onSpeedValueChanged);
|
||||
|
||||
connect(m_ui->slSpeed, &QSlider::valueChanged, this, [this](int val)
|
||||
{
|
||||
m_ui->lblSpeedVal->setText(QStringLiteral("%1").arg(val * 10.0, 0));
|
||||
m_speedValueChangedTimer->start(500);
|
||||
});
|
||||
|
||||
connect(m_ui->pbSetStart, &QPushButton::clicked,
|
||||
this, [this]()
|
||||
{
|
||||
emit signalSetStart(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
});
|
||||
|
||||
connect(m_ui->pbSetEnd, &QPushButton::clicked,
|
||||
this, [this]()
|
||||
{
|
||||
emit signalSetEnd(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
});
|
||||
|
||||
const QFontMetricsF fm(m_ui->lblSpeedVal->font());
|
||||
const QRectF rectSpeedVal = fm.boundingRect(QStringLiteral("000.0"));
|
||||
m_ui->lblSpeedVal->setFixedWidth(qRound(rectSpeedVal.width()));
|
||||
m_ui->slSpeed->setValue(10.0);
|
||||
|
||||
showEditorWidget(false);
|
||||
}
|
||||
|
||||
|
||||
SituationReplayControlWidget::~SituationReplayControlWidget()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
|
||||
bool SituationReplayControlWidget::init(LtsSession::LtsSession *session,
|
||||
const std::shared_ptr<Interface::Dtc> &dtc,
|
||||
const std::shared_ptr<Interface::Ftc> &ftc,
|
||||
const std::shared_ptr<MarkerManagement::Interface::Dtc> &mrkDtc, // can be 0 !
|
||||
const std::shared_ptr<SituationMap::ForeignGuiInterface> &sitMapGuiIfc, // can be 0 !
|
||||
int partitionId, bool useExistingReplay)
|
||||
{
|
||||
m_session = session;
|
||||
m_dtc = dtc;
|
||||
m_ftc = ftc;
|
||||
m_mrkDtc = mrkDtc; // can be 0 !
|
||||
m_partitionId = partitionId;
|
||||
m_sitMapGuiIfc = sitMapGuiIfc; // can be 0 !
|
||||
|
||||
if (!m_session->getDtc<ExerciseManagement::DataInterface>(m_exmDtc))
|
||||
{
|
||||
ERROR_LOG(m_logger)<<" couldn't init exmDtc!";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ui->pbStart->setText(tr("Start"));
|
||||
m_ui->pbStop->setText(tr("Stop"));
|
||||
m_ui->pbPause->setText(tr("Pause"));
|
||||
m_ui->pbStartPause->setToolTip(tr("Start"));
|
||||
|
||||
|
||||
m_dtc->waitForHistoricalDataSitReplay(20000);
|
||||
|
||||
|
||||
if (useExistingReplay)
|
||||
{
|
||||
useExistingReplay = false;
|
||||
|
||||
const QMap<qint32, SituationReplay::SitReplay> replays = m_dtc->sitReplays();
|
||||
for (const SituationReplay::SitReplay &replay : replays)
|
||||
{
|
||||
if (replay.replayPartitionId() == m_partitionId)
|
||||
{
|
||||
m_state = replay.state();
|
||||
m_id = replay.id();
|
||||
DEBUG_LOG(m_logger) << "recycling replay with id" << m_id;
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(replay.beginTime());
|
||||
useExistingReplay = true; // replay really exists
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_ui->dteStartTime->setRetrieveCurrentTimeCallback([]() { return QDateTime::currentMSecsSinceEpoch(); });
|
||||
enaDisa();
|
||||
|
||||
|
||||
connect(m_dtc.get(), &Interface::Dtc::sitReplayChanged,
|
||||
this, &SituationReplayControlWidget::onSitReplayChanged);
|
||||
// this, [this](const SituationReplay::SitReplay & _t1, DataModification _t2)
|
||||
// {
|
||||
// QTimer::singleShot(3000, this, [this, _t1, _t2]()
|
||||
// {
|
||||
// onSitReplayChanged(_t1, _t2);
|
||||
// });
|
||||
// });
|
||||
|
||||
connect(m_session, &LtsSession::LtsSession::partitionTimeChanged,
|
||||
this, &SituationReplayControlWidget::onPartitionTimeChanged);
|
||||
|
||||
connect(m_ui->slPeriodSlider, &QSlider::valueChanged, this,
|
||||
&SituationReplayControlWidget::onPeriodSliderChanged);
|
||||
connect(m_ui->dteStartTime, &LtsWidgets::LtsDateTimeEdit::dateTimeChanged, this,
|
||||
&SituationReplayControlWidget::onDteStartChanged);
|
||||
|
||||
|
||||
// set end time every new second
|
||||
QTimer *t = new QTimer(this);
|
||||
t->setInterval(200);
|
||||
|
||||
connect(t, &QTimer::timeout,
|
||||
this, [this]()
|
||||
{
|
||||
m_endTime = QDateTime::currentMSecsSinceEpoch();
|
||||
m_ui->lblEnd->setText(DTG::toStringMSec(m_endTime));
|
||||
m_ui->slPeriodSlider->setMaximum(getSecs(m_endTime));
|
||||
});
|
||||
|
||||
|
||||
const ExerciseManagement::Exercise &exercise = m_exmDtc->exercise(m_session->exerciseId());
|
||||
const qint64 stopTime = exercise.stopTime();
|
||||
if (stopTime > 0)
|
||||
{
|
||||
m_ui->lblEnd->setText(DTG::toStringMSec(stopTime));
|
||||
m_ui->slPeriodSlider->setMaximum(getSecs(stopTime));
|
||||
m_endTime = stopTime;
|
||||
}
|
||||
else
|
||||
t->start();
|
||||
|
||||
// set start time if start time not taken from existing replay
|
||||
// TODO: unclog this getDtc & waitForX mess
|
||||
if (!useExistingReplay)
|
||||
{
|
||||
std::shared_ptr<ExerciseManagement::Interface::Dtc> excDtc;
|
||||
if (session->getDtc<ExerciseManagement::DataInterface>(excDtc))
|
||||
{
|
||||
int excId = session->exerciseId();
|
||||
|
||||
excDtc->waitForHistoricalDataExercise(3000);
|
||||
ExerciseManagement::Exercise exc = excDtc->exercise(excId);
|
||||
qint64 startTime = exc.startTime();
|
||||
|
||||
if (startTime > 0)
|
||||
{
|
||||
//WARNING_LOG(m_logger) << "exc start time " << startTime;
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(startTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
qint64 beginTime = exc.beginTime();
|
||||
|
||||
if (beginTime > 0)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "no exc start time set, using exc begin time " << beginTime;
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(beginTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "no valid exc for getting exercise start time";
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(QDateTime::currentDateTime().addDays(-5).toMSecsSinceEpoch()); // 5 days ... just a guess
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << "no exc dtc for getting exercise start time";
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(QDateTime::currentDateTime().addDays(-5).toMSecsSinceEpoch()); // 5 days ... just a guess
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PeriodSlider, print start & end time
|
||||
qint64 startInSecs = getSecs(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
qint64 endInSecs = QDateTime::currentSecsSinceEpoch();
|
||||
|
||||
m_ui->slPeriodSlider->setMinimum(startInSecs);
|
||||
m_ui->slPeriodSlider->setMaximum(endInSecs);
|
||||
|
||||
{ // shorten lifetime of blocker
|
||||
QSignalBlocker blocker(m_ui->slPeriodSlider);
|
||||
m_ui->slPeriodSlider->setValue(startInSecs);
|
||||
}
|
||||
|
||||
m_ui->lblStart->setText(DTG::toStringMSec(getMSecs(startInSecs)));
|
||||
m_ui->lblEnd->setText(DTG::toStringMSec(getMSecs(endInSecs)));
|
||||
|
||||
|
||||
// Marker
|
||||
std::shared_ptr<OperatorManagement::Interface::Dtc> opmDtc;
|
||||
m_session->getDtc<OperatorManagement::DataInterface>(opmDtc);
|
||||
std::shared_ptr<FileManagement::ForeignGuiInterface> flmGui;
|
||||
m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(flmGui);
|
||||
m_markerModel = new MarkerManagement::MarkerManagementDialogModel(this);
|
||||
m_markerModel->initModel(m_mrkDtc, opmDtc, flmGui);
|
||||
m_ui->tvMarker->setModel(m_markerModel);
|
||||
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColImage);
|
||||
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColFileId);
|
||||
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColOffset);
|
||||
#ifndef LTS_DEBUG
|
||||
m_ui->tvMarker->hideColumn(MarkerManagement::MarkerManagementDialogModel::ColStreams);
|
||||
#endif
|
||||
m_ui->tvMarker->setSortIndicator(MarkerManagement::MarkerManagementDialogModel::ColTime, Qt::DescendingOrder); // newest marker first
|
||||
|
||||
// may be delayed because data must be intiially read from opm here
|
||||
const int curOpId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
|
||||
if (!m_ui->tvMarker->setInitialSearchContent(MarkerManagement::MarkerManagementDialogModel::ColOperator, curOpId))
|
||||
{
|
||||
QTimer::singleShot(200, this, [this, curOpId]
|
||||
{
|
||||
m_ui->tvMarker->setInitialSearchContent(MarkerManagement::MarkerManagementDialogModel::ColOperator, curOpId);
|
||||
});
|
||||
}
|
||||
|
||||
connect(m_ui->tvMarker, &LtsDataView::LtsDataView::activated, this, &SituationReplayControlWidget::onMarkerClicked);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
qint64 SituationReplayControlWidget::startTime() const
|
||||
{
|
||||
return m_ui->dteStartTime->msecsSinceEpoch();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::setStartTime(qint64 startTime)
|
||||
{
|
||||
ERROR_LOG(m_logger) << LOGFUNCTION << " " << startTime;
|
||||
if (startTime < 0)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << LOGFUNCTION << " called with invalid start time: " << startTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ui->dteStartTime->msecsSinceEpoch() == startTime)
|
||||
return;
|
||||
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(startTime);
|
||||
emit signalSetStart(startTime);
|
||||
}
|
||||
|
||||
|
||||
int SituationReplayControlWidget::speed() const
|
||||
{
|
||||
return m_ui->slSpeed->value();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::setSpeed(int speed)
|
||||
{
|
||||
m_ui->slSpeed->setValue(speed);
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::showEditorWidget(bool visibility)
|
||||
{
|
||||
m_ui->editorWidget->setVisible(visibility);
|
||||
}
|
||||
|
||||
|
||||
bool SituationReplayControlWidget::startReplay(qint64 start, qint64 end, double speed)
|
||||
{
|
||||
stopReplay();
|
||||
|
||||
disableAllReplayButtons(); // prevent the user from clicking again while waiting ...
|
||||
|
||||
QTimer::singleShot(1 * 647, this, [this, start, end, speed]()
|
||||
{
|
||||
startReplayDelayed(start, end, speed);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::disableAllReplayButtons()
|
||||
{
|
||||
m_ui->pbStart->setEnabled(false);
|
||||
m_ui->slPeriodSlider->setEnabled(false);
|
||||
m_ui->dteStartTime->setEnabled(false);
|
||||
m_ui->tvMarker->setEnabled(false);
|
||||
m_ui->pbStop->setEnabled(false);
|
||||
m_ui->pbPause->setEnabled(false);
|
||||
m_ui->pbStartPause->setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::pauseReplay()
|
||||
{
|
||||
if (m_sitMapGuiIfc != nullptr)
|
||||
m_sitMapGuiIfc->enableFireEventFlashing(false);
|
||||
|
||||
if (m_id == 0) // nothing 2 do
|
||||
{
|
||||
WARNING_LOG(m_logger) << "replay pause requested, but no replay active in this context";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state == SrpState::Resumed || m_state == SrpState::Started)
|
||||
{
|
||||
disableAllReplayButtons(); // prevent the user from clicking again while waiting ...
|
||||
|
||||
ChangeReplayStateRequest req;
|
||||
req.id(m_id);
|
||||
req.replayPartitionId(m_partitionId);
|
||||
req.state(SrpState::Paused);
|
||||
req.pace(m_ui->slSpeed->value() / 10.);
|
||||
|
||||
if (!m_requestHelper->startSingleRequest([req, this]()
|
||||
{
|
||||
return m_ftc->changeReplayStateRequest(req, this);
|
||||
}))
|
||||
{
|
||||
enaDisa(); // ... and bail out if operation didn't work out
|
||||
ERROR_LOG(m_logger) << "pausing failed for replay " << m_id << ": " << m_requestHelper->getErrorString();
|
||||
}
|
||||
else
|
||||
emit replayPaused();
|
||||
}
|
||||
else
|
||||
ERROR_LOG(m_logger) << "pause requested, but replay state was not resumed or started, but " << m_state << " for " << m_id;
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::stopReplay()
|
||||
{
|
||||
if (m_sitMapGuiIfc != nullptr)
|
||||
m_sitMapGuiIfc->enableFireEventFlashing(true);
|
||||
|
||||
if (m_id == 0) // nothing 2 do
|
||||
{
|
||||
WARNING_LOG(m_logger) << "replay stop requested, but no replay active in this context";
|
||||
return;
|
||||
}
|
||||
|
||||
disableAllReplayButtons(); // prevent the user from clicking again while waiting ...
|
||||
|
||||
ChangeReplayStateRequest req;
|
||||
req.id(m_id);
|
||||
req.replayPartitionId(m_partitionId);
|
||||
req.state(SrpState::Stopped);
|
||||
|
||||
// since this may potentially take some time, disable user interaction which may delete this widget for now
|
||||
const bool wasQuiet = m_requestHelper->quiet();
|
||||
m_requestHelper->setQuiet(false);
|
||||
if (m_requestHelper->startSingleRequest([req, this]()
|
||||
{
|
||||
return m_ftc->changeReplayStateRequest(req, this);
|
||||
}))
|
||||
{
|
||||
m_ui->pbStartPause->setChecked(false);
|
||||
m_ui->pbStartPause->setToolTip(tr("Start"));
|
||||
m_id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
enaDisa(); // ... and bail out if operation didn't work out
|
||||
ERROR_LOG(m_logger) << "stopping failed for replay " << m_id << ": " << m_requestHelper->getErrorString();
|
||||
}
|
||||
|
||||
m_requestHelper->setQuiet(wasQuiet);
|
||||
emit replayStopped();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::resumeReplay()
|
||||
{
|
||||
if (m_sitMapGuiIfc != nullptr)
|
||||
m_sitMapGuiIfc->enableFireEventFlashing(true);
|
||||
|
||||
disableAllReplayButtons(); // prevent the user from clicking again while waiting ...
|
||||
|
||||
ChangeReplayStateRequest req;
|
||||
req.id(m_id);
|
||||
req.replayPartitionId(m_partitionId);
|
||||
req.state(SrpState::Resumed);
|
||||
req.beginTime(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
req.endTime(0); // infinite
|
||||
req.pace(m_ui->slSpeed->value() / 10.);
|
||||
|
||||
if (!m_requestHelper->startSingleRequest([req, this]()
|
||||
{
|
||||
return m_ftc->changeReplayStateRequest(req, this);
|
||||
}))
|
||||
{
|
||||
enaDisa(); // ... and bail out if operation didn't work out
|
||||
ERROR_LOG(m_logger) << "resuming failed for replay " << m_id << ": " << m_requestHelper->getErrorString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onSitReplayChanged(const SitReplay &data, DataModification modType)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "onSitReplayChanged: id = " << data.id() << ", exId = " << data.exerciseId() << ", repPartitionId = " << data.replayPartitionId()
|
||||
<< ", state = " << (int)data.state().underlying() << ", m_partitionId = " << m_partitionId << ", modType = " << (int)modType;
|
||||
|
||||
if (m_partitionId != data.replayPartitionId())
|
||||
return;
|
||||
|
||||
if (modType == DataModification::eAdded ||
|
||||
modType == DataModification::eModified)
|
||||
m_state = data.state();
|
||||
else
|
||||
m_state = SrpState::Stopped;
|
||||
|
||||
enaDisa();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onPartitionTimeChanged(qint64 newPartitionTime)
|
||||
{
|
||||
const qint64 &partTime = getSecs(newPartitionTime);
|
||||
|
||||
if (partTime < getSecs(m_endTime) )
|
||||
m_ui->lblCurrentPartitionTime->setText(DTG::toStringMSec(newPartitionTime));
|
||||
else
|
||||
stopReplay();
|
||||
|
||||
m_ui->slPeriodSlider->setValue(getSecs(newPartitionTime));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @details Executed if the user double clicked on the Event in list. Set the map settings and focus the event on the map if the marker is an situation marker ans has an valid position
|
||||
* @param index
|
||||
*/
|
||||
void SituationReplayControlWidget::onMarkerClicked(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
MarkerManagement::Marker marker = m_markerModel->getRowData(index.row());
|
||||
const qint64 mSecsSinceEpoch = marker.timeStamp() - marker.timeStampOffset();
|
||||
m_ui->slPeriodSlider->setValue(getSecs(mSecsSinceEpoch));
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(mSecsSinceEpoch);
|
||||
|
||||
|
||||
MarkerManagement::MarkerManagementApplyFilter *appFilter = nullptr;
|
||||
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> fltParticipantIfc;
|
||||
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> fltAesIfc;
|
||||
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> fltBuildingComponentGuiIfc;
|
||||
if (m_session->getForeignGuiInterface(fltParticipantIfc) &&
|
||||
m_session->getForeignGuiInterface(fltAesIfc)
|
||||
// && m_session->getForeignGuiInterface(fltBuildingComponentGuiIfc) // Nachtrag zu GUEZE-1769: since fltBuildingComponentIfc is optional:
|
||||
)
|
||||
appFilter = new MarkerManagement::MarkerManagementApplyFilter(m_session, fltParticipantIfc, fltAesIfc, fltBuildingComponentGuiIfc);
|
||||
else
|
||||
WARNING_LOG(m_logger) << "unable to set filters from marker as flt ifc`s are missing";
|
||||
|
||||
|
||||
// set filter from marker
|
||||
if (marker.filter().size() > 0 && appFilter != nullptr)
|
||||
{
|
||||
QByteArray mapFilter(QByteArray::fromRawData(marker.filter().c_str(), marker.filter().size()));
|
||||
if (appFilter->applyFilter(mapFilter))
|
||||
emit signalSetMapFilter(mapFilter);
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Error while setting filters";
|
||||
}
|
||||
else
|
||||
{
|
||||
// this path will be taken if marker is created automatically (like CES)
|
||||
if (m_sitMapGuiIfc != nullptr)
|
||||
{
|
||||
const std::vector<MarkerManagement::MarkerDescription> &descriptionsRef = marker.descriptions();
|
||||
QList<MarkerManagement::MarkerType> markerTypes;
|
||||
markerTypes.reserve(descriptionsRef.size());
|
||||
for (const MarkerManagement::MarkerDescription &desc : qAsConst(descriptionsRef))
|
||||
markerTypes.append(desc.markerType());
|
||||
|
||||
if (markerTypes.contains(MarkerManagement::MarkerType_def::Situation))
|
||||
m_sitMapGuiIfc->setCenterPoint(QPointF(marker.position().lon(), marker.position().lat()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set map settings from marker
|
||||
if (appFilter)
|
||||
{
|
||||
QByteArray mapSettings(QByteArray::fromRawData(marker.mapSettings().c_str(), marker.mapSettings().size()));
|
||||
DEBUG_LOG(m_logger) << "set map settings from marker " << (mapSettings.toStdString());
|
||||
|
||||
// Layer EmergencyCall should not be modified.
|
||||
// So remove from data to be restored:
|
||||
SituationMap::OperatorMapSettings oms;
|
||||
oms.fromJson(mapSettings);
|
||||
DEBUG_LOG(m_logger) << ">>>> oms.orderedLayers.size() " << oms.orderedLayers.size();
|
||||
|
||||
static const QLatin1String LayerNameEmergencyCall("EmergencyCall");
|
||||
// remove entry: (EmergencyCall ist most likely at the end)
|
||||
auto pos = std::remove_if( oms.orderedLayers.begin(),
|
||||
oms.orderedLayers.end(),
|
||||
[](const QPair<QString, SituationMap::LayerParameter> &pair) { return pair.first == LayerNameEmergencyCall; } );
|
||||
DEBUG_LOG(m_logger) << ">>>> remove iterator: " << pos << " END: " << oms.orderedLayers.end();
|
||||
if(pos != oms.orderedLayers.end())
|
||||
oms.orderedLayers.erase(pos);
|
||||
|
||||
// write back:
|
||||
mapSettings = oms.toJson();
|
||||
|
||||
// apply: oms.orderedLayers.end()
|
||||
if (appFilter->applyMapSettings(mapSettings))
|
||||
emit signalSetMapSettings(mapSettings);
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Error while setting mapSettings";
|
||||
}
|
||||
delete appFilter;
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::enaDisa()
|
||||
{
|
||||
bool enaStart = false;
|
||||
bool enaStop = false;
|
||||
bool enaPause = false;
|
||||
|
||||
switch (m_state.underlying())
|
||||
{
|
||||
case SrpState::Paused:
|
||||
enaStart = false;
|
||||
enaStop = true;
|
||||
enaPause = false;
|
||||
break;
|
||||
case SrpState::Resumed:
|
||||
case SrpState::Started:
|
||||
enaStart = false;
|
||||
enaStop = true;
|
||||
enaPause = true;
|
||||
break;
|
||||
case SrpState::Unknown:
|
||||
case SrpState::Stopped:
|
||||
enaStart = true;
|
||||
enaStop = false;
|
||||
enaPause = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_ui->pbStart->setEnabled(enaStart);
|
||||
m_ui->slPeriodSlider->setEnabled(!enaPause);
|
||||
m_ui->dteStartTime->setEnabled(!enaPause);
|
||||
m_ui->tvMarker->setEnabled(enaStart);
|
||||
m_ui->pbStop->setEnabled(enaStop);
|
||||
m_ui->pbPause->setEnabled(enaPause);
|
||||
m_ui->pbStartPause->setEnabled(true);
|
||||
|
||||
QSignalBlocker sb(m_ui->pbStartPause);
|
||||
m_ui->pbStartPause->setChecked(enaPause);
|
||||
}
|
||||
|
||||
|
||||
bool SituationReplayControlWidget::startReplayDelayed(qint64 start, qint64 end, double speed)
|
||||
{
|
||||
SituationMap::SitMapUpdatesEnabledHelper sitMapHelper(m_sitMapGuiIfc);
|
||||
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(start);
|
||||
|
||||
{
|
||||
QSignalBlocker block(m_ui->slSpeed); // otherwise 'valueChanged' would do another request
|
||||
m_ui->slSpeed->setValue(qRound(speed * 10));
|
||||
}
|
||||
|
||||
ChangeReplayStateRequest req;
|
||||
req.id(m_id);
|
||||
req.replayPartitionId(m_partitionId);
|
||||
req.state(SrpState::Started);
|
||||
req.beginTime(start);
|
||||
req.endTime(end);
|
||||
req.pace(speed);
|
||||
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "Start Replay Partition: "
|
||||
<< "id: " << m_id
|
||||
<< ", partition id: " << m_partitionId
|
||||
<< ", partition type: " << m_session->partitionType()
|
||||
<< ", start: " << start
|
||||
<< ", end: " << end
|
||||
<< ", speed: " << speed;
|
||||
|
||||
if (m_requestHelper->startSingleRequest([req, this]()
|
||||
{
|
||||
return m_ftc->changeReplayStateRequest(req, this);
|
||||
}))
|
||||
{
|
||||
int repliedId = m_requestHelper->getRepliedId();
|
||||
|
||||
if (repliedId > 0)
|
||||
{
|
||||
m_id = repliedId;
|
||||
/*DEBUG*/ERROR_LOG(m_logger) << "Replied replay id: " << repliedId;
|
||||
emit replayStarted();
|
||||
return true;
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "replay start didn't return a replay id!";
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "could not start replay: " << m_requestHelper->getErrorString() << " (" << m_requestHelper->getRepliedId() << ")";
|
||||
enaDisa(); // ... bail (startReplay does a disableAllButtons(..))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onStartReplay()
|
||||
{
|
||||
double speed = m_ui->slSpeed->value();
|
||||
speed /= 10;
|
||||
qint64 startInSecs = getSecs(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
DEBUG_LOG(m_logger) << "onStartReplay startInSecs=" << startInSecs;
|
||||
m_ui->slPeriodSlider->setValue(startInSecs);
|
||||
|
||||
startReplay(m_ui->dteStartTime->msecsSinceEpoch(), 0 /* infinite */, speed);
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onStopReplay() // TODO: InterfaceImpl::startReplay needs this method to check if there is currently a replay running!!
|
||||
{
|
||||
stopReplay();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onPauseResumeReplay()
|
||||
{
|
||||
if (m_state == SrpState::Resumed || m_state == SrpState::Started)
|
||||
pauseReplay();
|
||||
else if (m_state == SrpState::Paused)
|
||||
resumeReplay();
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onStartPauseButtonToggled(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
if (m_state == SrpState::Paused)
|
||||
{
|
||||
onPauseResumeReplay();
|
||||
m_ui->pbStartPause->setToolTip(tr("Pause"));
|
||||
}
|
||||
else
|
||||
{
|
||||
onStartReplay();
|
||||
m_ui->pbStartPause->setToolTip(tr("Pause"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
onPauseResumeReplay();
|
||||
m_ui->pbStartPause->setToolTip(tr("Continue"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onSpeedValueChanged()
|
||||
{
|
||||
double speed = m_ui->slSpeed->value() / 10.;
|
||||
emit signalSetSpeed(speed);
|
||||
|
||||
if (m_id == 0 || (m_state != SrpState::Resumed && m_state != SrpState::Started)) // nothing 2 do
|
||||
return;
|
||||
|
||||
ChangeReplayStateRequest req;
|
||||
req.id(m_id);
|
||||
req.replayPartitionId(m_partitionId);
|
||||
req.pace(speed);
|
||||
|
||||
m_requestHelper->startSingleRequest([req, this]()
|
||||
{
|
||||
return m_ftc->changeReplayStateRequest(req, this);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onPeriodSliderChanged(int seconds)
|
||||
{
|
||||
m_ui->dteStartTime->setMsecsSinceEpoch(getMSecs(seconds));
|
||||
}
|
||||
|
||||
|
||||
void SituationReplayControlWidget::onDteStartChanged()
|
||||
{
|
||||
qint64 startInSecs = getSecs(m_ui->dteStartTime->msecsSinceEpoch());
|
||||
|
||||
QSignalBlocker block(m_ui->slPeriodSlider);
|
||||
m_ui->slPeriodSlider->setValue(startInSecs);
|
||||
}
|
||||
|
||||
|
||||
qint64 SituationReplayControlWidget::getMSecs(qint64 sec)
|
||||
{
|
||||
return sec * 1000;
|
||||
}
|
||||
|
||||
|
||||
qint64 SituationReplayControlWidget::getSecs(qint64 msec)
|
||||
{
|
||||
return msec / 1000;
|
||||
}
|
||||
|
||||
} // namespace SituationReplay
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Exportable widget with replay controls
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Mike Wille, NLE, 2019-10-02
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SituationReplay/srp_ftc.h>
|
||||
#include <SituationReplay/srp_dtc.h>
|
||||
#include <SituationReplay/logger.h>
|
||||
|
||||
#include <Plugins/MarkerManagement/mrk_data_plugin_interface.h>
|
||||
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
|
||||
|
||||
//SituationMap
|
||||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||||
|
||||
#include <LtsSession/lts_session.h>
|
||||
#include <LtsUtilsGuiDds/request_helper.h>
|
||||
|
||||
|
||||
namespace MarkerManagement
|
||||
{
|
||||
class MarkerManagementDialogModel;
|
||||
}
|
||||
namespace Ui
|
||||
{
|
||||
class SituationReplayControlWidget;
|
||||
}
|
||||
class QTimer;
|
||||
|
||||
|
||||
namespace SituationReplay
|
||||
{
|
||||
// --------------------------------------------------------
|
||||
// SituationReplayControlWidget
|
||||
// --------------------------------------------------------
|
||||
class SituationReplayControlWidget final : public QWidget
|
||||
, protected Interface::Ftc::ChangeReplayStateReplyHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SituationReplayControlWidget(QWidget *parent = nullptr);
|
||||
~SituationReplayControlWidget();
|
||||
|
||||
bool init(LtsSession::LtsSession *session,
|
||||
const std::shared_ptr<Interface::Dtc> &dtc,
|
||||
const std::shared_ptr<Interface::Ftc> &ftc,
|
||||
const std::shared_ptr<MarkerManagement::Interface::Dtc> &mrkDtc,
|
||||
const std::shared_ptr<SituationMap::ForeignGuiInterface> &sitMapGuiIfc,
|
||||
int partitionId, bool useExistingReplay = true);
|
||||
|
||||
void onChangeReplayStateReply(const Core::FunctionReplyObject &replyObject) override final
|
||||
{
|
||||
// QTimer::singleShot(3000, this, [this, replyObject]()
|
||||
// {
|
||||
m_requestHelper->requestReply(replyObject);
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
qint64 startTime() const;
|
||||
void setStartTime(qint64 startTime);
|
||||
|
||||
int speed() const;
|
||||
void setSpeed(int speed);
|
||||
|
||||
void showEditorWidget(bool visibility = true);
|
||||
|
||||
bool startReplay(qint64 start, qint64 end = 0 /* 0 = infinite */, double speed = 1.0f);
|
||||
void pauseReplay();
|
||||
void stopReplay();
|
||||
void resumeReplay();
|
||||
SrpState getReplayState() { return m_state; }
|
||||
int getId() { return m_id; }
|
||||
|
||||
signals:
|
||||
void signalSetStart(qint64 start);
|
||||
void signalSetEnd(qint64 end);
|
||||
void signalSetSpeed(double speed);
|
||||
void signalSetMapFilter(const QByteArray &mapFilter);
|
||||
void signalSetMapSettings(const QByteArray &mapSettings);
|
||||
void replayStarted();
|
||||
void replayPaused();
|
||||
void replayStopped();
|
||||
|
||||
private slots:
|
||||
void onSitReplayChanged(const SitReplay &data, DataModification modType);
|
||||
void onPartitionTimeChanged(qint64 newPartitionTime);
|
||||
void onMarkerClicked(const QModelIndex &index);
|
||||
void enaDisa();
|
||||
bool startReplayDelayed(qint64 start, qint64 end = 0 /* 0 = infinite */, double speed = 1.0f);
|
||||
void onStartReplay();
|
||||
void onStopReplay();
|
||||
void onPauseResumeReplay();
|
||||
void onStartPauseButtonToggled(bool checked); // if true means we start play
|
||||
void onSpeedValueChanged();
|
||||
void onPeriodSliderChanged(int seconds);
|
||||
void onDteStartChanged();
|
||||
|
||||
private:
|
||||
void disableAllReplayButtons();
|
||||
qint64 getMSecs(qint64 sec);
|
||||
qint64 getSecs(qint64 msec);
|
||||
|
||||
Ui::SituationReplayControlWidget *m_ui;
|
||||
LtsSession::LtsSession *m_session;
|
||||
std::shared_ptr<SituationReplay::Interface::Dtc> m_dtc;
|
||||
std::shared_ptr<SituationReplay::Interface::Ftc> m_ftc;
|
||||
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
|
||||
MarkerManagement::MarkerManagementDialogModel *m_markerModel;
|
||||
std::shared_ptr<SituationMap::ForeignGuiInterface> m_sitMapGuiIfc;
|
||||
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
|
||||
LtsUtils::RequestHelper *m_requestHelper;
|
||||
int m_id;
|
||||
int m_partitionId;
|
||||
SrpState m_state;
|
||||
qint64 m_endTime;
|
||||
bool m_toggleStartPauseButton; // if true, only one button is used for Start/Pause
|
||||
QTimer *m_speedValueChangedTimer;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace SituationReplay
|
||||
@@ -0,0 +1,361 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SituationReplayControlWidget</class>
|
||||
<widget class="QWidget" name="SituationReplayControlWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>849</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Replay Control</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Current Partition Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblCurrentPartitionTime">
|
||||
<property name="text">
|
||||
<string notr="true">-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LtsDataView::LtsDataView" name="tvMarker" native="true">
|
||||
<property name="showColumnCount" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonsVisible" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblStart">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="slPeriodSlider">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::NoTicks</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblEnd">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QToolButton" name="pbStartPause">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>|> / ||</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/avm_start.svg</normaloff>
|
||||
<normalon>:/style/avm_pause.svg</normalon>:/style/avm_start.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="pbStart">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">|></string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/avm_start.svg</normaloff>:/style/avm_start.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="pbPause">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">||</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/avm_pause.svg</normaloff>:/style/avm_pause.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="pbStop">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">[]</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/style/avm_stop.svg</normaloff>:/style/avm_stop.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblStartTime">
|
||||
<property name="text">
|
||||
<string>Start Time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LtsWidgets::LtsDateTimeEdit" name="dteStartTime" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="withNowButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Speed [%]:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblSpeedVal">
|
||||
<property name="text">
|
||||
<string notr="true"><VALUE></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="slSpeed">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>160</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="editorWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set time as..:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbSetStart">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbSetEnd">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>End</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LtsWidgets::LtsDateTimeEdit</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsWidgets/lts_date_time_edit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LtsDataView::LtsDataView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LtsDataView/lts_data_view.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>slPeriodSlider</tabstop>
|
||||
<tabstop>pbStartPause</tabstop>
|
||||
<tabstop>pbStart</tabstop>
|
||||
<tabstop>pbPause</tabstop>
|
||||
<tabstop>pbStop</tabstop>
|
||||
<tabstop>dteStartTime</tabstop>
|
||||
<tabstop>slSpeed</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in new issue
Block a user