DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
QDynExpLineGraph.cpp
Go to the documentation of this file.
1// This file is part of DynExp.
2
3#include "QDynExpLineGraph.h"
4
5namespace DynExpQuick
6{
8 : Name(std::move(Name)),
9 BarSeries(std::make_unique<QBarSeries>()), LineSeries(std::make_unique<QLineSeries>()),
10 BarSet(new QBarSet(this->Name))
11 {
12 BarSet->append(.0);
13 BarSeries->append(BarSet);
14 }
15
16 size_t DynExpLineGraphPlotModel::InsertSeries(QString Name, QColor Color)
17 {
18 int iIndex = Util::NumToT<int>(PlotSeries.size());
19
20 beginInsertRows({}, iIndex, iIndex);
21 PlotSeries.push_back({ Name });
22 PlotSeries.back().LineSeries->setColor(Color.isValid() ? Color : DynExpUI::PlotColors::ColorFromIndex(PlotSeries.size() - 1));
23 PlotSeries.back().BarSet->setColor(Color.isValid() ? Color : DynExpUI::PlotColors::ColorFromIndex(PlotSeries.size() - 1));
24 endInsertRows();
25
26 return PlotSeries.size() - 1;
27 }
28
30 {
31 if (Index < 0 || Index >= PlotSeries.size())
32 throw Util::OutOfRangeException("Index exceeds the bounds of PlotSeries.");
33
34 int iIndex = Util::NumToT<int>(Index);
35
36 beginRemoveRows({}, iIndex, iIndex);
37 PlotSeries.erase(PlotSeries.begin() + Index);
38 endRemoveRows();
39 }
40
42 {
43 QStringList SeriesNames;
44
45 for (const auto& Series : PlotSeries)
46 SeriesNames.push_back(QString::fromStdString(DynExp::Object::RemoveCategoryAndName(Series.Name.toStdString())));
47
48 return SeriesNames;
49 }
50
51 size_t DynExpLineGraphPlotModel::GetIndex(QString Name) const
52 {
53 size_t Index = -1;
54
55 for (size_t i = 0; i < PlotSeries.size(); ++i)
56 {
57 if (PlotSeries.at(i).Name == Name)
58 {
59 Index = i;
60
61 break;
62 }
63 }
64
65 return Index;
66 }
67
69 {
70 if (Index < 0 || Index >= PlotSeries.size())
71 throw Util::OutOfRangeException("Index exceeds the bounds of PlotSeries.");
72
73 return PlotSeries.at(Index);
74 }
75
77 {
78 return const_cast<DynExpLineGraphPlotSeries&>(std::as_const(*this).GetSeries(Index));
79 }
80
82 {
83 return std::any_of(PlotSeries.cbegin(), PlotSeries.cend(), [](auto& Series) { return Series.LineSeries->isVisible(); });
84 }
85
87 {
88 if (Index < 0 || Index >= PlotSeries.size())
89 throw Util::OutOfRangeException("Index exceeds the bounds of PlotSeries.");
90
91 int iIndex = Util::NumToT<int>(Index);
92
93 emit dataChanged(QAbstractItemModel::createIndex(iIndex, 0), QAbstractItemModel::createIndex(iIndex, 0));
94 }
95
96 QHash<int, QByteArray> DynExpLineGraphPlotModel::roleNames() const
97 {
98 return {
99 { NameRole, "name" },
100 { VisibleRole, "visible" },
101 { NumSamplesRole, "numsamples" },
102 { ColorRole, "color" }
103 };
104 }
105
106 Qt::ItemFlags DynExpLineGraphPlotModel::flags(const QModelIndex&) const
107 {
108 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsEditable;
109 }
110
111 int DynExpLineGraphPlotModel::rowCount(const QModelIndex&) const
112 {
113 return Util::NumToT<int>(PlotSeries.size());
114 }
115
116 QVariant DynExpLineGraphPlotModel::data(const QModelIndex& index, int role) const
117 {
118 if (!index.isValid())
119 return {};
120
121 const auto& Series = PlotSeries.at(index.row());
122
123 switch (role)
124 {
125 case NameRole: return Series.Name;
126 case VisibleRole: return Series.Visible;
127 case NumSamplesRole: return Series.LineSeries->points().size();
128 case ColorRole: return Series.LineSeries->color();
129 }
130
131 return {};
132 }
133
134 bool DynExpLineGraphPlotModel::setData(const QModelIndex& index, const QVariant& value, int role)
135 {
136 if (!index.isValid())
137 return false;
138
139 auto& Series = PlotSeries[index.row()];
140
141 switch (role)
142 {
143 case VisibleRole: Series.Visible = value.toBool(); break;
144 default: return false;
145 }
146
147 emit dataChanged(index, index, { role });
148
149 return true;
150 }
151
152 DynExpLineGraphBackend::DynExpLineGraphBackend(QObject* parent) : QObject(parent),
153 XCategoryAxis(std::make_unique<QBarCategoryAxis>()), XValueAxis(std::make_unique<QValueAxis>()),
154 YValueAxis(std::make_unique<QValueAxis>())
155 {
156 XValueAxis->setSubGridVisible(false);
157 YValueAxis->setSubGridVisible(false);
158 }
159
160 void DynExpLineGraphBackend::SetCursorPosition(QPointF CursorPosition) noexcept
161 {
162 if (this->CursorPosition == CursorPosition)
163 return;
164
165 this->CursorPosition = CursorPosition;
166
167 emit qcursorPositionChanged(CursorPosition);
168 }
169
170 void DynExpLineGraphBackend::SetHoveredPoint(QPointF HoveredPoint) noexcept
171 {
172 if (this->HoveredPoint == HoveredPoint)
173 return;
174
175 this->HoveredPoint = HoveredPoint;
176
177 emit qhoveredPointChanged(HoveredPoint);
178 }
179
180 void DynExpLineGraphBackend::SetHoveredSample(QPointF HoveredSample) noexcept
181 {
182 if (this->HoveredSample == HoveredSample)
183 return;
184
185 this->HoveredSample = HoveredSample;
186
187 emit qhoveredSampleChanged(HoveredSample);
188 }
189
190 void DynExpLineGraphBackend::InsertSeries(QString Name, QColor Color)
191 {
192 const auto Index = PlotModel.InsertSeries(Name, Color);
193 auto& Series = PlotModel.GetSeries(Index);
194
195 emit qinsertSeries(Series.BarSeries.get(), Series.LineSeries.get());
196 }
197
199 {
200 const auto Index = PlotModel.GetIndex(Name);
201 if (Index < 0)
202 return;
203
204 auto& Series = PlotModel.GetSeries(Index);
205
206 emit qremoveSeries(Series.BarSeries.get(), Series.LineSeries.get());
207 PlotModel.RemoveSeries(Index);
208 }
209
210 const DynExpLineGraphPlotSeries& DynExpLineGraphBackend::UpdateSeries(size_t Index, const QList<QPointF>& Samples,
211 const DynExpModule::Graph::LineGraphPlotInfo& PlotInfo, bool UpdateSamples)
212 {
213 auto& Series = GetSeries(Index);
214
215 if (Series.Visible)
216 {
217 if (UpdateSamples)
218 {
219 if (!Samples.empty())
220 {
221 Series.BarSet->replace(0, std::abs(Samples.front().y()));
222 Series.LineSeries->replace(Samples);
223 }
224 else
225 {
226 // Circumvent QML bug that does not update empty series. This point is not plotted.
227 Series.LineSeries->replace({ {0., 0.} });
228 }
229 }
230
231 Series.LineSeries->setWidth(!PlotInfo.HoveredPoint.isNull() && PlotInfo.HoveredSeries == Index ? 4 : 2);
232 }
233
234 Series.BarSeries->setVisible(Series.Visible && Samples.size() == 1);
235 Series.LineSeries->setVisible(Series.Visible && Samples.size() > 1);
236
237 SeriesChanged(Index);
238
239 return Series;
240 }
241
243 bool Autoscale, bool UpdateAxes)
244 {
245 const bool AnyLineSeriesVisible = PlotModel.IsAnyLineSeriesVisible();
246
247 if (UpdateAxes)
248 {
249 XCategoryAxis->setVisible(!AnyLineSeriesVisible);
250 XValueAxis->setVisible(AnyLineSeriesVisible);
251 if (AnyLineSeriesVisible)
252 {
253 if (PlotInfo.XLabel.isEmpty())
254 XValueAxis->setTitleText(QString(PlotInfo.XIsLogarithmic ? "log. " : "") + DynExp::Units::UnitCategoryToStr(PlotInfo.XUnit) + " [" +
255 (PlotInfo.XUnit == DynExp::Units::UnitType::Time_s ? PlotInfo.GetMultiplierLabel() + "s" : DynExp::Units::UnitTypeToStr(PlotInfo.XUnit)) + "]");
256 else
257 XValueAxis->setTitleText(PlotInfo.XLabel);
258 XValueAxis->setLabelFormat(DynExp::Units::IsIntegerUnit(PlotInfo.XUnit) ? "%.0f" : "%.3f");
259 XValueAxis->setRange(PlotInfo.MinValues.x(), PlotInfo.MaxValues.x());
260 }
261 else
262 XCategoryAxis->setCategories(PlotModel.GetSeriesNames());
263
264 YValueAxis->setTitleText(PlotInfo.YLabel.isEmpty() ?
265 ((PlotInfo.YIsLogarithmic ? "log. " : "") + QString::fromStdString(DynExp::Units::UnitToStr(PlotInfo.YUnit))) : PlotInfo.YLabel);
266 YValueAxis->setLabelFormat(DynExp::Units::IsIntegerUnit(PlotInfo.YUnit) ? "%.0f" : "%.3f");
268 {
269 if (Autoscale)
270 YValueAxis->setRange(0, 1);
271 YValueAxis->setTickInterval(1);
272 }
273 else
274 {
275 if (Autoscale)
276 YValueAxis->setRange(PlotInfo.MinValues.y(), PlotInfo.MaxValues.y());
277 YValueAxis->setTickInterval(0);
278 }
279
280 emit qdataChanged(AnyLineSeriesVisible);
281 }
282
283 if (AnyLineSeriesVisible)
284 {
287 }
288
290 }
291}
C++ backend of the QDynExpLineGraph QML component.
const DynExpLineGraphPlotSeries & UpdateSeries(size_t Index, const QList< QPointF > &Samples, const DynExpModule::Graph::LineGraphPlotInfo &PlotInfo, bool UpdateSamples=true)
void UpdateData(DynExpModule::Graph::LineGraphPlotInfo &PlotInfo, bool Autoscale=true, bool UpdateAxes=true)
QML_ELEMENTDynExpLineGraphPlotModel * PlotModel
QPointF GetCursorPosition() const noexcept
void SetHoveredPoint(QPointF HoveredPoint) noexcept
DynExpLineGraphBackend(QObject *parent=nullptr)
void SetHoveredSample(QPointF HoveredSample) noexcept
void qremoveSeries(QBarSeries *, QLineSeries *)
void SetCursorPosition(QPointF CursorPosition) noexcept
void qinsertSeries(QBarSeries *, QLineSeries *)
void InsertSeries(QString Name, QColor Color={})
const auto & GetSeries(size_t Index) const
const DynExpLineGraphPlotSeries & GetSeries(size_t Index) const
std::vector< DynExpLineGraphPlotSeries > PlotSeries
QHash< int, QByteArray > roleNames() const override
Qt::ItemFlags flags(const QModelIndex &) const override
size_t InsertSeries(QString Name, QColor Color)
QVariant data(const QModelIndex &index, int role) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
int rowCount(const QModelIndex &) const override
static std::string RemoveCategoryAndName(const std::string &ObjectDesc)
Removes the category and name part built by Object::CategoryAndNameToStr() from the string ObjectDesc...
Definition Object.cpp:358
Thrown when an argument passed to a function exceeds the valid range.
Definition Exception.h:212
constexpr QColor ColorFromIndex(size_t Index)
Returns the plot color from the cycle list to be assigned to the plot with index Index.
@ LogicLevel
Logic level (TTL) units (1 or 0)
std::string UnitToStr(UnitType Unit)
Returns a descriptive string of a respective unit category and type to be e.g. used in plots.
Definition Units.cpp:135
const char * UnitCategoryToStr(UnitType Unit)
Returns a descriptive string of the given unit's category (e.g. 'intensity').
Definition Units.cpp:73
bool IsIntegerUnit(UnitType Unit)
Checks whether Unit implies integer values.
Definition Units.cpp:7
const char * UnitTypeToStr(UnitType Unit)
Returns a descriptive string of the given unit's type (e.g. 'dBm').
Definition Units.cpp:104
DynExp::Units::UnitType XUnit
Joint unit of the plot's x axis.
Definition GraphUtil.h:104
QPointF CursorPosition
Relative position of the mouse cursor inside the coordinate system (between 0. and 1....
Definition GraphUtil.h:165
QPointF HoveredPoint
Hovered point belonging to any series in normalized coordinate system coordinates (between 0....
Definition GraphUtil.h:170
size_t HoveredSeries
Index of the data series the hovered point belongs to.
Definition GraphUtil.h:180
QPointF MinValues
Current lower axes limits of x and y axes.
Definition GraphUtil.h:145
QString YLabel
If empty, the y label is constructed from YUnit. Otherwise, YLabel is used.
Definition GraphUtil.h:129
QString GetMultiplierLabel() const
Returns the multiplier prefix associated with Multiplier.
Definition GraphUtil.cpp:7
bool XIsLogarithmic
Determines whether x axis is logarithmic.
Definition GraphUtil.h:114
QPointF HoveredSample
Hovered sample belonging to any series in the graphs's real coordinate system.
Definition GraphUtil.h:175
bool YIsLogarithmic
Determines whether y axis is logarithmic.
Definition GraphUtil.h:119
DynExp::Units::UnitType YUnit
Joint unit of the plot's y axis.
Definition GraphUtil.h:109
QString XLabel
If empty, the x label is constructed from XUnit. Otherwise, XLabel is used.
Definition GraphUtil.h:124
QPointF MaxValues
Current upper axes limits of x and y axes.
Definition GraphUtil.h:150
std::shared_ptr< QBarSeries > BarSeries