4 #include "moc_SignalPlotter.cpp"
10 : QModuleWidget(Owner, parent),
11 PlotContextMenu(new QMenu(this)),
12 DataSeries(nullptr), DataChart(nullptr), XAxis(nullptr), YAxis(nullptr)
26 ui.Signal->setRenderHint(QPainter::Antialiasing);
29 ui.action_Run->setChecked(
true);
35 DataChart->axes()[1]->setLabelsEditable(!IsRunning);
70 if (SampleData.
Points.empty())
73 if (SampleData.
Points.size() > 1)
83 auto ScatterSeries =
new QScatterSeries(
this);
84 ScatterSeries->append(SampleData.
Points);
85 ScatterSeries->setMarkerShape(QScatterSeries::MarkerShape::MarkerShapeCircle);
86 ScatterSeries->setMarkerSize(15);
96 auto Factor = std::abs(SampleData.
MinValues.y()) * .01;
125 if (Filename.isEmpty())
128 std::stringstream CSVData;
130 for (
int i = 0; i < Util::NumToT<int>(
DataSeries->count()); ++i)
134 QMessageBox::warning(
this,
"DynExp - Error",
"Error writing data to file.");
161 CurrentSourceIndex = Index;
163 ValueUnit = GetDataStreamInstr()->GetValueUnit();
164 UIInitialized =
false;
174 UIInitialized =
false;
175 CurrentSourceIndex = 0;
177 IsBasicSampleTimeUsed =
false;
179 PlotAxesChanged =
true;
188 std::vector<DynExpInstr::BasicSample> BasicSamples;
189 bool IsBasicSampleTimeUsed{};
193 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.
ModuleDataGetter());
195 if (ModuleData->IsRunning)
197 ModuleData->GetDataStreamInstr()->ReadData();
199 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr()->GetInstrumentData());
200 auto SampleStream = InstrData->GetSampleStream();
202 if (!ModuleData->RollingView || !SampleStream->SeekEqual(std::ios_base::in))
203 SampleStream->SeekBeg(std::ios_base::in);
204 BasicSamples = SampleStream->ReadBasicSamples(SampleStream->GetStreamSizeRead());
205 IsBasicSampleTimeUsed = SampleStream->IsBasicSampleTimeUsed();
208 NumFailedUpdateAttempts = 0;
212 if (NumFailedUpdateAttempts++ >= 3)
216 if (ProcessBasicSamples(std::move(BasicSamples), IsBasicSampleTimeUsed))
217 IsBasicSampleTimeUsed =
false;
220 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.
ModuleDataGetter());
222 if (ModuleData->IsBasicSampleTimeUsed != IsBasicSampleTimeUsed && ModuleData->IsRunning)
224 ModuleData->IsBasicSampleTimeUsed = IsBasicSampleTimeUsed;
225 ModuleData->PlotAxesChanged =
true;
234 NumFailedUpdateAttempts = 0;
239 auto Widget = std::make_unique<SignalPlotterWidget>(*
this);
252 auto Widget = GetWidget<SignalPlotterWidget>();
253 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(ModuleDataGetter());
255 if (!Widget->ui.CBSource->count())
257 const QSignalBlocker CBSourceBlocker(Widget->ui.CBSource);
259 for (
const auto& InstrLabel : ModuleData->GetDataStreamInstrLabels())
260 Widget->ui.CBSource->addItem(QIcon(ModuleData->GetDataStreamInstrIconPath().data()), QString::fromStdString(InstrLabel));
262 Widget->ui.CBSource->setCurrentIndex(0);
263 Widget->ui.CBSource->setVisible(Widget->ui.CBSource->count() > 1);
266 if (!ModuleData->IsUIInitialized())
268 Widget->GetAutoscalePlotAction()->setChecked(ModuleData->Autoscale);
269 Widget->GetRollingViewPlotAction()->setChecked(ModuleData->RollingView);
271 auto*
XAxis =
new QValueAxis(Widget);
272 auto*
YAxis =
new QValueAxis(Widget);
276 YAxis->setLabelFormat(
"%d");
277 YAxis->setRange(0, 1);
278 YAxis->setTickCount(2);
279 YAxis->setMinorTickCount(0);
280 YAxis->setMinorGridLineVisible(
false);
285 ModuleData->PlotAxesChanged =
true;
286 ModuleData->SetUIInitialized();
289 if (ModuleData->PlotAxesChanged ||
290 (ModuleData->IsRunning && ModuleData->SampleData.Multiplier != Widget->GetMultiplier() && !ModuleData->SampleData.Points.empty()))
292 if (Widget->GetXAxis())
294 Widget->GetXAxis()->setTitleText(ModuleData->IsBasicSampleTimeUsed ?
295 "time in " + ModuleData->SampleData.GetMultiplierLabel() +
"s" :
"sample in #");
296 Widget->GetXAxis()->setLabelFormat(ModuleData->IsBasicSampleTimeUsed ?
"%.3f" :
"%d");
299 ModuleData->PlotAxesChanged =
false;
302 if (ModuleData->IsRunning)
304 Widget->SetData(ModuleData->SampleData);
305 Widget->ui.LNumSamples->setText(QString::number(ModuleData->SampleData.Points.size()) +
" sample"
306 + (ModuleData->SampleData.Points.size() == 1 ?
"" :
"s"));
309 Widget->UpdateUI(ModuleData->IsRunning);
314 bool FallenBackToNotUseSampleTime =
false;
317 if (BasicSamples.empty())
319 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(GetModuleData());
320 ModuleData->SampleData.Reset();
322 return FallenBackToNotUseSampleTime;
326 if (IsBasicSampleTimeUsed)
330 std::stable_sort(BasicSamples.begin(), BasicSamples.end(), [](
const auto& a,
const auto& b) {
331 return a.Time < b.Time;
335 if (BasicSamples.front().Time == BasicSamples.back().Time && BasicSamples.size() > 1)
337 IsBasicSampleTimeUsed =
false;
338 FallenBackToNotUseSampleTime =
true;
342 if (std::abs(BasicSamples.front().Time) < 1.0 && std::abs(BasicSamples.back().Time) < 1.0)
345 if (std::abs(BasicSamples.front().Time) < 1e-3 && std::abs(BasicSamples.back().Time) < 1e-3)
348 if (std::abs(BasicSamples.front().Time) < 1e-6 && std::abs(BasicSamples.back().Time) < 1e-6)
355 double YMin(std::numeric_limits<double>::max()), YMax(std::numeric_limits<double>::lowest());
357 for (
size_t i = 0; i < BasicSamples.size(); ++i)
359 auto Y = BasicSamples[i].Value;
360 SampleData.
Points.append({ IsBasicSampleTimeUsed ? BasicSamples[i].Time * std::pow(10.0,
Multiplier) : i,
Y });
362 YMin = std::min(YMin,
Y);
363 YMax = std::max(YMax,
Y);
370 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(GetModuleData());
371 ModuleData->SampleData = std::move(SampleData);
373 return FallenBackToNotUseSampleTime;
378 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(Instance->
ParamsGetter());
379 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
381 ModuleData->LockInstruments(Instance, ModuleParams->DataStreamInstr);
383 ModuleData->ValueUnit = ModuleData->GetDataStreamInstr()->GetValueUnit();
384 ModuleData->Autoscale = ModuleParams->Autoscale.Get();
385 ModuleData->RollingView = ModuleParams->RollingView.Get();
390 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
392 ModuleData->UnlockInstruments(Instance);
397 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
400 ModuleData->GetDataStreamInstr()->ResetStreamSize();
402 ModuleData->IsRunning = Checked;
407 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
409 ModuleData->SetCurrentSourceIndex(Index);
414 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
415 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
417 ModuleParams->Autoscale = Checked;
418 ModuleData->Autoscale = Checked;
423 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
424 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
426 ModuleParams->RollingView = Checked;
427 ModuleData->RollingView = Checked;
432 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->
ModuleDataGetter());
433 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr()->GetInstrumentData());
435 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...
@ Y
Y component of the signal in cartesian coordinates.
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.