4#include "moc_SignalPlotter.cpp"
5#include "ui_SignalPlotter.h"
11 : QModuleWidget(Owner, parent),
13 PlotContextMenu(new QMenu(this)),
14 DataSeries(nullptr), DataChart(nullptr), XAxis(nullptr), YAxis(nullptr)
28 ui->Signal->setRenderHint(QPainter::Antialiasing);
31 ui->action_Run->setChecked(
true);
37 DataChart->axes()[1]->setLabelsEditable(!IsRunning);
72 if (SampleData.
Points.empty())
75 if (SampleData.
Points.size() > 1)
85 auto ScatterSeries =
new QScatterSeries(
this);
86 ScatterSeries->append(SampleData.
Points);
87 ScatterSeries->setMarkerShape(QScatterSeries::MarkerShape::MarkerShapeCircle);
88 ScatterSeries->setMarkerSize(15);
98 auto Factor = std::abs(SampleData.
MinValues.y()) * .01;
127 if (Filename.isEmpty())
130 std::stringstream CSVData;
132 for (
int i = 0; i < Util::NumToT<int>(
DataSeries->count()); ++i)
136 QMessageBox::warning(
this,
"DynExp - Error",
"Error writing data to file.");
163 CurrentSourceIndex = Index;
165 ValueUnit = GetDataStreamInstr()->GetValueUnit();
166 UIInitialized =
false;
176 UIInitialized =
false;
177 CurrentSourceIndex = 0;
179 IsBasicSampleTimeUsed =
false;
181 PlotAxesChanged =
true;
190 std::vector<DynExpInstr::BasicSample> BasicSamples;
191 bool IsBasicSampleTimeUsed{};
195 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.
ModuleDataGetter());
197 if (ModuleData->IsRunning)
199 ModuleData->GetDataStreamInstr()->ReadData();
201 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr()->GetInstrumentData());
202 auto SampleStream = InstrData->GetSampleStream();
204 if (!ModuleData->RollingView || !SampleStream->SeekEqual(std::ios_base::in))
205 SampleStream->SeekBeg(std::ios_base::in);
206 BasicSamples = SampleStream->ReadBasicSamples(SampleStream->GetStreamSizeRead());
207 IsBasicSampleTimeUsed = SampleStream->IsBasicSampleTimeUsed();
210 NumFailedUpdateAttempts = 0;
214 if (NumFailedUpdateAttempts++ >= 3)
218 if (ProcessBasicSamples(std::move(BasicSamples), IsBasicSampleTimeUsed))
219 IsBasicSampleTimeUsed =
false;
222 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.
ModuleDataGetter());
224 if (ModuleData->IsBasicSampleTimeUsed != IsBasicSampleTimeUsed && ModuleData->IsRunning)
226 ModuleData->IsBasicSampleTimeUsed = IsBasicSampleTimeUsed;
227 ModuleData->PlotAxesChanged =
true;
236 NumFailedUpdateAttempts = 0;
241 auto Widget = std::make_unique<SignalPlotterWidget>(*
this);
254 auto Widget = GetWidget<SignalPlotterWidget>();
255 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(ModuleDataGetter());
257 if (!Widget->ui->CBSource->count())
259 const QSignalBlocker CBSourceBlocker(Widget->ui->CBSource);
261 for (
const auto& InstrLabel : ModuleData->GetDataStreamInstrLabels())
262 Widget->ui->CBSource->addItem(QIcon(ModuleData->GetDataStreamInstrIconPath().data()), QString::fromStdString(InstrLabel));
264 Widget->ui->CBSource->setCurrentIndex(0);
265 Widget->ui->CBSource->setVisible(Widget->ui->CBSource->count() > 1);
268 if (!ModuleData->IsUIInitialized())
270 Widget->GetAutoscalePlotAction()->setChecked(ModuleData->Autoscale);
271 Widget->GetRollingViewPlotAction()->setChecked(ModuleData->RollingView);
273 auto*
XAxis =
new QValueAxis(Widget);
274 auto*
YAxis =
new QValueAxis(Widget);
278 YAxis->setLabelFormat(
"%d");
279 YAxis->setRange(0, 1);
280 YAxis->setTickCount(2);
281 YAxis->setMinorTickCount(0);
282 YAxis->setMinorGridLineVisible(
false);
287 ModuleData->PlotAxesChanged =
true;
288 ModuleData->SetUIInitialized();
291 if (ModuleData->PlotAxesChanged ||
292 (ModuleData->IsRunning && ModuleData->SampleData.Multiplier != Widget->GetMultiplier() && !ModuleData->SampleData.Points.empty()))
294 if (Widget->GetXAxis())
296 Widget->GetXAxis()->setTitleText(ModuleData->IsBasicSampleTimeUsed ?
297 "time in " + ModuleData->SampleData.GetMultiplierLabel() +
"s" :
"sample in #");
298 Widget->GetXAxis()->setLabelFormat(ModuleData->IsBasicSampleTimeUsed ?
"%.3f" :
"%d");
301 ModuleData->PlotAxesChanged =
false;
304 if (ModuleData->IsRunning)
306 Widget->SetData(ModuleData->SampleData);
307 Widget->ui->LNumSamples->setText(QString::number(ModuleData->SampleData.Points.size()) +
" sample"
308 + (ModuleData->SampleData.Points.size() == 1 ?
"" :
"s"));
311 Widget->UpdateUI(ModuleData->IsRunning);
316 bool FallenBackToNotUseSampleTime =
false;
319 if (BasicSamples.empty())
321 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(GetModuleData());
322 ModuleData->SampleData.Reset();
324 return FallenBackToNotUseSampleTime;
328 if (IsBasicSampleTimeUsed)
332 std::stable_sort(BasicSamples.begin(), BasicSamples.end(), [](
const auto& a,
const auto& b) {
333 return a.Time < b.Time;
337 if (BasicSamples.front().Time == BasicSamples.back().Time && BasicSamples.size() > 1)
339 IsBasicSampleTimeUsed =
false;
340 FallenBackToNotUseSampleTime =
true;
344 if (std::abs(BasicSamples.front().Time) < 1.0 && std::abs(BasicSamples.back().Time) < 1.0)
347 if (std::abs(BasicSamples.front().Time) < 1e-3 && std::abs(BasicSamples.back().Time) < 1e-3)
350 if (std::abs(BasicSamples.front().Time) < 1e-6 && std::abs(BasicSamples.back().Time) < 1e-6)
357 double YMin(std::numeric_limits<double>::max()), YMax(std::numeric_limits<double>::lowest());
359 for (
size_t i = 0; i < BasicSamples.size(); ++i)
361 auto Y = BasicSamples[i].Value;
362 SampleData.
Points.append({ IsBasicSampleTimeUsed ? BasicSamples[i].Time * std::pow(10.0,
Multiplier) : i, Y });
364 YMin = std::min(YMin, Y);
365 YMax = std::max(YMax, Y);
372 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(GetModuleData());
373 ModuleData->SampleData = std::move(SampleData);
375 return FallenBackToNotUseSampleTime;
380 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(Instance->
ParamsGetter());
381 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
383 ModuleData->LockInstruments(Instance, ModuleParams->DataStreamInstr);
385 ModuleData->ValueUnit = ModuleData->GetDataStreamInstr()->GetValueUnit();
386 ModuleData->Autoscale = ModuleParams->Autoscale.Get();
387 ModuleData->RollingView = ModuleParams->RollingView.Get();
392 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
394 ModuleData->UnlockInstruments(Instance);
399 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
402 ModuleData->GetDataStreamInstr()->ResetStreamSize();
404 ModuleData->IsRunning = Checked;
409 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
411 ModuleData->SetCurrentSourceIndex(Index);
416 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
417 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
419 ModuleParams->Autoscale = Checked;
420 ModuleData->Autoscale = Checked;
425 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
426 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
428 ModuleParams->RollingView = Checked;
429 ModuleData->RollingView = Checked;
434 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
435 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr()->GetInstrumentData());
437 InstrData->GetSampleStream()->Clear();
Implementation of a module to plot the samples stored in data stream instruments.
@ Arbitrary
Arbitrary units (a.u.)
@ LogicLevel
Logic level (TTL) units (1 or 0)
static const char * UnitTypeToStr(const UnitType &Unit)
Returns a descriptive string of a respective unit to be e.g. used in plots.
void ResetImpl(dispatch_tag< QModuleDataBase >) override final
void SetCurrentSourceIndex(int Index)
Util::DynExpErrorCodes::DynExpErrorCodes ModuleMainLoop(DynExp::ModuleInstance &Instance) override final
Module main loop. The function is executed periodically by the module thread. Also refer to GetMainLo...
void OnExit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread terminates (not due to an exception,...
void OnPlotRollingViewClicked(DynExp::ModuleInstance *Instance, bool Checked) const
void OnRunClicked(DynExp::ModuleInstance *Instance, bool Checked) const
void ResetImpl(dispatch_tag< QModuleBase >) override final
void OnSourceChanged(DynExp::ModuleInstance *Instance, int Index) const
bool ProcessBasicSamples(std::vector< DynExpInstr::BasicSample > &&BasicSamples, bool IsBasicSampleTimeUsed)
Converts BasicSamples to displayable format.
std::unique_ptr< DynExp::QModuleWidget > MakeUIWidget() override final
Used by InitUI() as a factory function for the module's user interface widget. Create the widget here...
void OnInit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread starts. Override it to lock instruments this m...
void OnPlotAutoscaleClicked(DynExp::ModuleInstance *Instance, bool Checked) const
void OnClearStream(DynExp::ModuleInstance *Instance, bool) const
void UpdateUIChild(const ModuleBase::ModuleDataGetterType &ModuleDataGetter) override final
Refer to ParamsBase::dispatch_tag.
Defines data for a thread belonging to a ModuleBase instance. Refer to RunnableInstance.
const ModuleBase::ModuleDataGetterType ModuleDataGetter
Getter for module's data. Refer to ModuleBase::ModuleDataGetterType.
Refer to ParamsBase::dispatch_tag.
const Object::ParamsGetterType ParamsGetter
Invoke to obtain the parameters (derived from ParamsBase) of Owner.
const auto & GetOwner() const noexcept
Returns Owner.
Holds a CallableMemberWrapper and invokes its callable when being destroyed.
Thrown when an operation timed out before it could be completed, especially used for locking shared d...
DynExp's module namespace contains the implementation of DynExp modules which extend DynExp's core fu...
constexpr auto DefaultQChartTheme
DynExpErrorCodes
DynExp's error codes
bool SaveToFile(const QString &Filename, std::string_view Text)
Saves a std::string_view to a file (using QFile). Creates a new file or truncates an existing file's ...
QString PromptSaveFilePathModule(DynExp::QModuleWidget *Parent, const QString &Title, const QString &DefaultSuffix, const QString &NameFilter)
Works as PromptOpenFilePath() but asks the user to select a single file which does not need to exist....
Accumulates include statements to provide a precompiled header.